From b56555cc12975978ff0fa5895f37df9bb4d44cb0 Mon Sep 17 00:00:00 2001 From: latlon team <info@lat-lon.de> Date: Mon, 30 Sep 2024 05:25:39 +0000 Subject: [PATCH] Code drop - Merged in feature/improveHtmlReport-XPLANBOX-3413 (pull request #370) (861d82b00) - XPLANBOX-3408 image for XPlanValidatorWeb operations manual updated (c484e3ecf) - XPLANBOX-1050 - support of MultiGeometries in GeoJsonGeometryBuilder (85d2b8263) - XPLANBOX-1050 - fixed download of geojson (5749e7243) - XPLANBOX-3411 - Add new test GET BP 5.2 XX retrieveGeomfindings and implement assertions for geomfindings (6a5e8e8ab) - XPLANBOX-3423 - Add HTTP status code 406 to /{uuid}/geomfindings and /{uuid}/geomfindings.json (e4034354a) - XPLANBOX-3411 - Implement tests for geomfindings in XPlanManagerAPI v2 TestSuite (dc721242b) - XPLANBOX-3411 - Enhance and improve assertions of GET XX X.X XX openAPI (6d49513ee) - XPLANBOX-1050 - transform markerGeom in GeoJSON to WGS84 (60ec777de) - XPLANBOX-1050 - removed /geomfindings.geojson, added description of 406 (bfab70bf9) - XPLANBOX-3374 - upgrade postgis/postgis:14-3.4 (c66aa5f7a) - XPLANBOX-1354 - aggregated redundant flaechenschluss failures (409038022) - XPLANBOX-3411 - Implement /report/{uuid}/geomfindings TestCase and /report/{uuid}/geomfindings.json TestCase and add assertions to GET XX X.X XX openAPI (bcbf4d7ba) - fixed expected sized of xplan-validator-executor (6660078b1) - XPLANBOX-3347 - fixed PlanInfo link urls in v2, added/improved tests and javadoc (9c1fb7a1d) - XPLANBOX-3347 - fixed PlanInfo links to self in v2 (a414dc198) - XPLANBOX-3347 - fixed PlanInfo links in v1 if requestMediaType is a JSON sub type (e77bf5f8d) - XPLANBOX-3396 - Fix assertions for links to /plan/{planId} in XPlanManagerAPI v2 (1f72ef93c) - XPLANBOX-3396 - Implement assertions for links to /plan/{planId} (749a151a9) - XPLANBOX-1050 - added type to GeoJson Feature and FeatureCollection (a38a1f246) - XPLANBOX-1050 - added GEOJSON to zip (XPlanManagerAPI, XPlanValidatorAPI) (8a0784623) - XPLANBOX-1050 - refactoring: renamd ArtifactType to ReportFormatType (3a13c3f56) - XPLANBOX-1050 - added download of GeoJson, added to Zip (73e215ecc) - XPLANBOX-1050 - fixed tests (b6386448d) - XPLANBOX-1050 - added geomfindings paths to ReportApi (948129c06) - XPLANBOX-3347 - Corrected assertions and created mew ones for affected teststeps of the xplan-manager-api SoapUi project (4dc731acf) - XPLANBOX-1050 - removed hint to shape file from validation message (e47a7dffa) - XPLANBOX-1050 - added geomfindings to v2 VaidationReport (b5b540012) - XPLANBOX-1050 - implemented GeoJsonBuilder, moved GeoJsonGeometryBuilder to xplan-core-validator (b866f633a) - XPLANBOX-1050 - moved geojson model to xplan-core-validator, added Feature and Featureollection (cdbf5d0e1) - XPLANBOX-3347 - fixed links to PlanInfo (4b6afb3f8) Co-authored-by: Dirk Stenger <stenger@lat-lon.de> Co-authored-by: Julian Zilz <zilz@lat-lon.de> Co-authored-by: Lyn Elisa Goltz <goltz@lat-lon.de> Co-authored-by: Torsten Friebe <torsten.friebe@gmail.com> Dropped from commit: d3858c3d190f2c9347547922c788b4c76c2ea376 --- pom.xml | 2 +- .../api/commons/ValidationReportBuilder.java | 8 +- .../commons/v2/model/ValidationReport.java | 17 + ...alidationResultGeometrischRuleFinding.java | 2 +- .../commons/v2/model/geojson/Geometry.java | 147 - .../commons/ValidationReportBuilderTest.java | 37 +- .../ValidationReportConverterTest.java | 6 +- .../v2/model/ValidationReportTest.java | 6 +- ...rtifactType.java => ReportFormatType.java} | 4 +- .../client/ValidatorWebCommonsMessages.java | 2 + .../client/report/ReportDownloadPanel.java | 37 +- .../client/report/ReportUrlBuilder.java | 25 +- .../server/service/ReportController.java | 4 +- .../server/service/ReportProvider.java | 4 +- .../ValidatorWebCommonsMessages.properties | 5 +- xplan-core/xplan-core-validator/pom.xml | 9 + .../geometric/ValidationResultContext.java | 8 + .../FlaechenschlussFinding.java | 67 + .../OptimisedFlaechenschlussInspector.java | 77 +- .../result/GeometricValidationFinding.java | 2 +- .../xplan/validator/report/ReportWriter.java | 55 +- .../report/geojson/GeoJsonBuilder.java | 98 + .../geojson}/GeoJsonGeometryBuilder.java | 47 +- .../report/geojson/model/Feature.java | 150 + .../geojson/model/FeatureCollection.java | 109 + .../report/geojson/model/GeoJsonObject.java | 204 ++ .../report/geojson/model/Geometry.java | 67 + .../geojson/model}/GeometryCollection.java | 4 +- .../geojson/model}/GeometryElement.java | 2 +- .../report/geojson/model}/LineString.java | 4 +- .../geojson/model}/LineStringCoordinates.java | 2 +- .../report/geojson/model}/LinearRing.java | 2 +- .../geojson/model}/MultiLineString.java | 4 +- .../report/geojson/model}/MultiPoint.java | 4 +- .../report/geojson/model}/MultiPolygon.java | 4 +- .../report/geojson/model}/Point.java | 4 +- .../report/geojson/model}/Polygon.java | 4 +- .../report/geojson/model}/Position.java | 2 +- .../validator/report/pdf/ReportBuilder.java | 2 +- .../i18n/validationMessages.properties | 19 +- .../src/main/resources/xslt/report.xslt | 80 +- ...OptimisedFlaechenschlussInspectorTest.java | 55 +- .../validator/report/ReportWriterTest.java | 2 +- .../report/geojson/GeoJsonBuilderTest.java | 160 + .../geojson}/GeoJsonGeometryBuilderTest.java | 213 +- .../report/html/HtmlReportGeneratorTest.java | 22 +- .../xplan-database-docker/Dockerfile | 4 +- .../Validierungsbericht_download.png | Bin 6705 -> 28244 bytes xplan-manager/xplan-manager-api/pom.xml | 5 + .../xplanbox/api/manager/PlanInfoBuilder.java | 13 +- .../manager/openapi/AbstractApiConfig.java | 4 +- .../api/manager/openapi/v1/ApiV1Config.java | 4 +- .../api/manager/openapi/v2/ApiV2Config.java | 4 +- .../xplanbox/api/manager/v1/PlanApi.java | 49 +- .../xplanbox/api/manager/v1/PlansApi.java | 1 + .../xplanbox/api/manager/v2/PlanApi2.java | 4 +- .../xplanbox/api/manager/v2/ReportApi.java | 51 + .../src/main/resources/application.properties | 2 +- .../api/manager/config/TestContext.java | 37 +- .../xplanbox/api/manager/v1/PlanApiTest.java | 8 +- .../xplanbox/api/manager/v2/PlanApi2Test.java | 6 +- .../api/manager/v2/ReportApiTest.java | 14 + .../server/service/ManagerReportProvider.java | 4 +- xplan-tests/xplan-tests-integration/README.md | 2 + .../validatorweb/XPlanValidatorWebIT.java | 4 +- .../xplan-manager-api-soapui-project.xml | 2108 +++++++++++-- .../xplan-validator-api-soapui-project.xml | 502 +++- .../xplanbox/api/validator/v2/ReportApi.java | 51 + .../api/validator/v2/DefaultApi2Test.java | 3 +- .../api/validator/v2/ReportApiTest.java | 16 +- .../xplan-validator-executor/pom.xml | 4 +- .../validator/executor/PlanValidator.java | 17 +- .../executor/handler/ValidationHandler.java | 7 +- .../validator/executor/report1.expected.json | 2670 +++++++++-------- .../validator/executor/report2.expected.json | 234 ++ .../validator/executor/report3.expected.json | 234 ++ .../storage/ValidationExecutionStorage.java | 2 +- .../service/ValidatorReportProvider.java | 4 +- 78 files changed, 5832 insertions(+), 2024 deletions(-) delete mode 100644 xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Geometry.java rename xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/{ArtifactType.java => ReportFormatType.java} (95%) create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/FlaechenschlussFinding.java create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilder.java rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson}/GeoJsonGeometryBuilder.java (76%) create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Feature.java create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/FeatureCollection.java create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeoJsonObject.java create mode 100644 xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Geometry.java rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/GeometryCollection.java (95%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/GeometryElement.java (97%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/LineString.java (94%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/LineStringCoordinates.java (97%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/LinearRing.java (97%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/MultiLineString.java (95%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/MultiPoint.java (95%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/MultiPolygon.java (95%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/Point.java (94%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/Polygon.java (95%) rename xplan-core/{xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson => xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model}/Position.java (98%) create mode 100644 xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilderTest.java rename xplan-core/{xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons => xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson}/GeoJsonGeometryBuilderTest.java (50%) diff --git a/pom.xml b/pom.xml index 4df96d9010..9bbfdb93ac 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ <owasp.version>10.0.4</owasp.version> <swagger.version>2.2.23</swagger.version> <!-- overwrite from spring-boot-dependencies --> - <selenium.version>4.24.0</selenium.version> + <selenium.version>4.25.0</selenium.version> <!-- project properties --> <base.package.name>de.latlon.xplanbox</base.package.name> <distribution.repo.id>gitlab-maven</distribution.repo.id> diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/ValidationReportBuilder.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/ValidationReportBuilder.java index 83dfc0973c..7bd5191a7f 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/ValidationReportBuilder.java +++ b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/ValidationReportBuilder.java @@ -20,6 +20,7 @@ */ package de.latlon.xplanbox.api.commons; +import static de.latlon.xplan.validator.report.geojson.GeoJsonBuilder.createGeoJsonFailures; import static de.latlon.xplanbox.api.commons.DateConverter.convertToLocalDateTime; import static de.latlon.xplanbox.api.commons.v1.model.VersionEnum.fromXPlanVersion; import static de.latlon.xplanbox.api.commons.v2.model.ValidationRuleLevel.ERROR; @@ -40,9 +41,11 @@ import java.util.stream.Collectors; import de.latlon.xplan.validator.geometric.report.GeometricValidatorResult; import de.latlon.xplan.validator.geometric.result.GeometricValidationRule; import de.latlon.xplan.validator.planinfo.PlanInfo; +import de.latlon.xplan.validator.report.ReportGenerationException; import de.latlon.xplan.validator.report.SkipCode; import de.latlon.xplan.validator.report.ValidatorReport; import de.latlon.xplan.validator.report.ValidatorResult; +import de.latlon.xplan.validator.report.geojson.GeoJsonGeometryBuilder; import de.latlon.xplan.validator.report.reference.ExternalReferenceReport; import de.latlon.xplan.validator.semantic.report.InvalidFeaturesResult; import de.latlon.xplan.validator.semantic.report.SemanticValidatorResult; @@ -101,7 +104,7 @@ public class ValidationReportBuilder { return this; } - public ValidationReport build() { + public ValidationReport build() throws ReportGenerationException { requireNonNull(validatorReport); return new de.latlon.xplanbox.api.commons.v2.model.ValidationReport().filename(filename) .validationName(validatorReport.getValidationName()) @@ -109,7 +112,8 @@ public class ValidationReportBuilder { .status(status()) .wmsUrl(wmsUrl) .syntaktisch(syntaktischResult()) - .plans(validationReportPlans()); + .plans(validationReportPlans()) + .geomfindings(createGeoJsonFailures(validatorReport)); } private List<ValidationReportPlan> validationReportPlans() { diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReport.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReport.java index d0a257fa17..a1480be20a 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReport.java +++ b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReport.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.annotation.JsonInclude; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.xml.bind.annotation.XmlAccessType; @@ -57,6 +58,8 @@ public class ValidationReport { private @Valid List<ValidationReportPlan> plans = new ArrayList<>(); + private @Valid FeatureCollection geomfindings; + public ValidationReport filename(String filename) { this.filename = filename; return this; @@ -169,6 +172,19 @@ public class ValidationReport { this.plans = plans; } + public ValidationReport geomfindings(FeatureCollection geomfindings) { + this.geomfindings = geomfindings; + return this; + } + + public @Valid FeatureCollection getGeomfindings() { + return geomfindings; + } + + public void setGeomfindings(@Valid FeatureCollection geomfindings) { + this.geomfindings = geomfindings; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -180,6 +196,7 @@ public class ValidationReport { sb.append(" wmsUrl: ").append(toIndentedString(wmsUrl)).append("\n"); sb.append(" syntaktisch: ").append(toIndentedString(syntaktisch)).append("\n"); sb.append(" plans: ").append(toIndentedString(plans)).append("\n"); + sb.append(" geomfindings: ").append(toIndentedString(geomfindings)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportValidationResultGeometrischRuleFinding.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportValidationResultGeometrischRuleFinding.java index 11190e3693..f9f78953a5 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportValidationResultGeometrischRuleFinding.java +++ b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportValidationResultGeometrischRuleFinding.java @@ -3,7 +3,7 @@ package de.latlon.xplanbox.api.commons.v2.model; import java.util.List; import java.util.Objects; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry; +import de.latlon.xplan.validator.report.geojson.model.Geometry; import jakarta.validation.Valid; /** diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Geometry.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Geometry.java deleted file mode 100644 index b4a51c422a..0000000000 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Geometry.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * GeoJSON format - * This document defines the GeoJSON format as an OpenAPI. It contains the definitions for 'Feature' object and 'FeatureCollection' objects, as well as the definitions for all 'Geometry' objects. It conforms with the 'RFC-7946' standard from IETF (August 2016 version) Kudos to @bubbobne and @idkw whose code helped me not start from scratch https://gist.github.com/bubbobne/fe5f2db65acf039be6a9fd92fc9c7233 - * - * OpenAPI spec version: 1.0.1 - * Contact: zitoun@gmail.com - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ - -package de.latlon.xplanbox.api.commons.v2.model.geojson; - -import java.util.Objects; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonValue; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.annotation.Generated; -import jakarta.validation.constraints.NotNull; - -/** - * Abstract type for all GeoJSon object except Feature and FeatureCollection. Generated - * from OpenAPI document * https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1 - * - * @since 8.0 - */ -@Schema(description = "Abstract type for all GeoJSon object except Feature and FeatureCollection ") -@Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", - date = "2024-09-09T05:36:15.967036504Z[GMT]") -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") -@JsonSubTypes({ @JsonSubTypes.Type(value = GeometryCollection.class, name = "GeometryCollection"), - @JsonSubTypes.Type(value = Point.class, name = "Point"), - @JsonSubTypes.Type(value = LineString.class, name = "LineString"), - @JsonSubTypes.Type(value = Polygon.class, name = "Polygon"), - @JsonSubTypes.Type(value = MultiPoint.class, name = "MultiPoint"), - @JsonSubTypes.Type(value = MultiLineString.class, name = "MultiLineString"), - @JsonSubTypes.Type(value = MultiPolygon.class, name = "MultiPolygon") }) -public class Geometry { - - /** - * Gets or Sets type - */ - public enum TypeEnum { - - POINT("Point"), - - MULTIPOINT("MultiPoint"), - - LINESTRING("LineString"), - - MULTILINESTRING("MultiLineString"), - - POLYGON("Polygon"), - - MULTIPOLYGON("MultiPolygon"), - - GEOMETRYCOLLECTION("GeometryCollection"); - - private String value; - - TypeEnum(String value) { - this.value = value; - } - - @Override - @JsonValue - public String toString() { - return String.valueOf(value); - } - - @JsonCreator - public static TypeEnum fromValue(String text) { - for (TypeEnum b : TypeEnum.values()) { - if (String.valueOf(b.value).equals(text)) { - return b; - } - } - return null; - } - - } - - @JsonProperty("type") - private TypeEnum type = null; - - public Geometry type(TypeEnum type) { - this.type = type; - return this; - } - - /** - * Get type - * @return type - **/ - @JsonProperty("type") - @Schema(required = true, description = "") - @NotNull - public TypeEnum getType() { - return type; - } - - public void setType(TypeEnum type) { - this.type = type; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - return true; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode()); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class Geometry {\n"); - sb.append(" ").append(toIndentedString(super.toString())).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(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - -} diff --git a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportBuilderTest.java b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportBuilderTest.java index 22082eab14..e3063fdda4 100644 --- a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportBuilderTest.java +++ b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportBuilderTest.java @@ -46,6 +46,7 @@ import de.latlon.xplan.validator.geometric.result.GeometricValidationFindingLeve import de.latlon.xplan.validator.geometric.result.GeometricValidationRule; import de.latlon.xplan.validator.planinfo.PlanInfo; import de.latlon.xplan.validator.planinfo.PlanInfoReport; +import de.latlon.xplan.validator.report.ReportGenerationException; import de.latlon.xplan.validator.report.SkipCode; import de.latlon.xplan.validator.report.ValidatorReport; import de.latlon.xplan.validator.report.reference.ExternalReferenceReport; @@ -72,14 +73,14 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_ReturnsInstance() { + void verifyThat_Builder_ReturnsInstance() throws ReportGenerationException { ValidatorReport sourceReport = mock(ValidatorReport.class); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport).build(); assertNotNull(report); } @Test - void verifyThat_Builder_AddsFilename() { + void verifyThat_Builder_AddsFilename() throws ReportGenerationException { ValidatorReport sourceReport = mock(ValidatorReport.class); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) .filename("test.xml") @@ -88,7 +89,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_AddsVersion() { + void verifyThat_Builder_AddsVersion() throws ReportGenerationException { ValidatorReport sourceReport = createSourceReport(); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport).build(); assertThat(report.getPlans().size()).isEqualTo(1); @@ -96,7 +97,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_AddsWmsUrl() throws URISyntaxException { + void verifyThat_Builder_AddsWmsUrl() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = mock(ValidatorReport.class); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) .wmsUrl(new URI("file://here")) @@ -105,7 +106,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_AddRasterEvaluationResult_v1() throws URISyntaxException { + void verifyThat_Builder_AddRasterEvaluationResult_v1() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = mockSourceReportWithRasterEvaluation(); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) @@ -118,7 +119,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_AddRasterEvaluationResult_v2() throws URISyntaxException { + void verifyThat_Builder_AddRasterEvaluationResult_v2() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = mockSourceReportWithRasterEvaluation(); de.latlon.xplanbox.api.commons.v2.model.ValidationReport report = new ValidationReportBuilder() @@ -132,7 +133,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_ReturnsCompleteInstance() throws URISyntaxException { + void verifyThat_Builder_ReturnsCompleteInstance() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = mock(ValidatorReport.class); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) .filename("test.xml") @@ -143,7 +144,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_statusSemanticError() throws URISyntaxException { + void verifyThat_Builder_statusSemanticError() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = createSourceReport(); SyntacticValidatorResult syntacticValidatorResult = new SyntacticValidatorResult(emptyList(), null); @@ -174,6 +175,7 @@ class ValidationReportBuilderTest { assertThat(report.getStatus()).isEqualTo(COMPLETED); assertThat(report.getPlans().size()).isEqualTo(1); + assertThat(report.getGeomfindings()).isNull(); assertThat(report.getPlans().get(0).getValid()).isFalse(); assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getStatus()).isEqualTo(COMPLETED); @@ -200,7 +202,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_statusGeometricError() throws URISyntaxException { + void verifyThat_Builder_statusGeometricError() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = createSourceReport(); SyntacticValidatorResult syntacticValidatorResult = new SyntacticValidatorResult(emptyList(), null); @@ -231,6 +233,7 @@ class ValidationReportBuilderTest { assertThat(report.getStatus()).isEqualTo(COMPLETED); assertThat(report.getPlans().size()).isEqualTo(1); + assertThat(report.getGeomfindings()).isNotNull(); assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getStatus()).isEqualTo(COMPLETED); assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getValid()).isTrue(); @@ -262,7 +265,7 @@ class ValidationReportBuilderTest { } @Test - void verifyThat_Builder_statusSkip() { + void verifyThat_Builder_statusSkip() throws ReportGenerationException { ValidatorReport sourceReport = createSourceReport(); SyntacticValidatorResult syntacticValidatorResult = new SyntacticValidatorResult(emptyList(), null); SemanticValidatorResult semanticValidatorResult = new SemanticValidatorResult(SkipCode.SKIPPED); @@ -282,12 +285,13 @@ class ValidationReportBuilderTest { assertThat(report.getStatus()).isEqualTo(COMPLETED); assertThat(report.getPlans().size()).isEqualTo(1); + assertThat(report.getGeomfindings()).isNull(); assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getStatus()).isEqualTo(SKIPPED); assertThat(report.getPlans().get(0).getValidationResult().getGeometrisch().getStatus()).isEqualTo(SKIPPED); } @Test - void verifyThat_Builder_statusSyntaxError() throws URISyntaxException { + void verifyThat_Builder_statusSyntaxError() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = createSourceReportWithSyntaxError(); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) @@ -297,13 +301,7 @@ class ValidationReportBuilderTest { assertThat(report.getStatus()).isEqualTo(TERMINATED_WITH_ERRORS); assertThat(report.getPlans().size()).isEqualTo(0); - // assertThat(report.getPlans().get(0).getValidationResult().getSyntaktisch().getStatus()).isEqualTo(COMPLETED); - // assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getStatus()) - // .isEqualTo(TERMINATED_WITH_ERRORS); - // assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getValid()).isNull(); - // assertThat(report.getPlans().get(0).getValidationResult().getGeometrisch().getStatus()) - // .isEqualTo(TERMINATED_WITH_ERRORS); - // assertThat(report.getPlans().get(0).getValidationResult().getGeometrisch().getValid()).isNull(); + assertThat(report.getGeomfindings()).isNull(); de.latlon.xplanbox.api.commons.v1.model.ValidationReport reportV1 = ValidationReportConverter .convertToV1(report); @@ -313,7 +311,7 @@ class ValidationReportBuilderTest { @Disabled("Improve test: sourceReport does not contain an error!") @Test - void verifyThat_Builder_statusError() throws URISyntaxException { + void verifyThat_Builder_statusError() throws URISyntaxException, ReportGenerationException { ValidatorReport sourceReport = createSourceReport(); ValidationReport report = new ValidationReportBuilder().validatorReport(sourceReport) @@ -322,6 +320,7 @@ class ValidationReportBuilderTest { .build(); assertThat(report.getStatus()).isEqualTo(TERMINATED_REASON_UNKNOWN); + assertThat(report.getGeomfindings()).isNull(); assertThat(report.getSyntaktisch()).isEqualTo(TERMINATED_REASON_UNKNOWN); assertThat(report.getPlans().size()).isEqualTo(1); assertThat(report.getPlans().get(0).getValidationResult().getSemantisch().getStatus()) diff --git a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportConverterTest.java b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportConverterTest.java index b4599ef7e2..42bd7bf8ff 100644 --- a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportConverterTest.java +++ b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/ValidationReportConverterTest.java @@ -56,9 +56,9 @@ import de.latlon.xplanbox.api.commons.v2.model.ValidationReportValidationResultS import de.latlon.xplanbox.api.commons.v2.model.ValidationReportValidationResultSemantischRuleFinding; import de.latlon.xplanbox.api.commons.v2.model.ValidationReportValidationResultSemantischWithRulesMetadata; import de.latlon.xplanbox.api.commons.v2.model.ValidationReportValidationResultSyntaktisch; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Point; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Position; +import de.latlon.xplan.validator.report.geojson.model.Geometry; +import de.latlon.xplan.validator.report.geojson.model.Point; +import de.latlon.xplan.validator.report.geojson.model.Position; import org.junit.jupiter.api.Test; /** diff --git a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportTest.java b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportTest.java index 3c9fad1fe2..5985ad4f1f 100644 --- a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportTest.java +++ b/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/v2/model/ValidationReportTest.java @@ -28,9 +28,9 @@ import de.latlon.xplanbox.api.commons.ValidationReportConverter; import de.latlon.xplanbox.api.commons.v1.model.ExternalReferenceResult; import de.latlon.xplanbox.api.commons.v1.model.RasterEvaluationResult; import de.latlon.xplanbox.api.commons.v1.model.RulesMetadata; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Point; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Position; +import de.latlon.xplan.validator.report.geojson.model.Geometry; +import de.latlon.xplan.validator.report.geojson.model.Point; +import de.latlon.xplan.validator.report.geojson.model.Position; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; diff --git a/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ArtifactType.java b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ReportFormatType.java similarity index 95% rename from xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ArtifactType.java rename to xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ReportFormatType.java index 3a8fdf5969..7a79f180ee 100644 --- a/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ArtifactType.java +++ b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/validator/web/shared/ReportFormatType.java @@ -23,8 +23,8 @@ package de.latlon.xplan.validator.web.shared; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ -public enum ArtifactType { +public enum ReportFormatType { - HTML, PDF + HTML, PDF, GEOJSON } diff --git a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.java b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.java index c00a773e3e..ce10d7235e 100644 --- a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.java +++ b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.java @@ -92,6 +92,8 @@ public interface ValidatorWebCommonsMessages extends Messages { String reportDownloadPdf(); + String reportDownloadGeoJson(); + String reportDownloadGeometryErrors(); String reportDownloadButtonTitle(); diff --git a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportDownloadPanel.java b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportDownloadPanel.java index 1336f96022..f920e3ee2c 100644 --- a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportDownloadPanel.java +++ b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportDownloadPanel.java @@ -20,6 +20,13 @@ */ package de.latlon.xplanbox.core.gwt.commons.client.report; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.GEOJSON; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.HTML; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.PDF; + +import java.util.ArrayList; +import java.util.List; + import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -30,16 +37,10 @@ import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; import de.latlon.xplanbox.core.gwt.commons.client.ValidatorWebCommonsMessages; import de.latlon.xplanbox.core.gwt.commons.shared.ValidationSummary; -import java.util.ArrayList; -import java.util.List; - -import static de.latlon.xplan.validator.web.shared.ArtifactType.HTML; -import static de.latlon.xplan.validator.web.shared.ArtifactType.PDF; - /** * Encapulates the download options. * @@ -54,6 +55,8 @@ public class ReportDownloadPanel extends CaptionPanel { private final CheckBox pdfCheckBox = new CheckBox(messages.reportDownloadPdf()); + private final CheckBox geojsonCheckBox = new CheckBox(messages.reportDownloadGeoJson()); + private final ReportUrlBuilder urlBuilder = new ReportUrlBuilder(); private ValidationSummary validationSummary; @@ -72,7 +75,7 @@ public class ReportDownloadPanel extends CaptionPanel { mainPanel.add(pdfCheckBox); mainPanel.add(createGeometryErrorSeperator()); - + mainPanel.add(geojsonCheckBox); mainPanel.add(createDownloadButton()); setContentWidget(mainPanel); @@ -88,9 +91,9 @@ public class ReportDownloadPanel extends CaptionPanel { download.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - List<ArtifactType> selectedArtifacts = getSelectedArtifacts(); - if (selectedArtifacts.size() > 0) { - String zipUrl = urlBuilder.createZipUrl(validationSummary, selectedArtifacts); + List<ReportFormatType> selectedReportFormats = getSelectedReportFormats(); + if (selectedReportFormats.size() > 0) { + String zipUrl = urlBuilder.createZipUrl(validationSummary, selectedReportFormats); GWT.log("Requested URL to receive the zip file with reports: " + zipUrl); Window.open(zipUrl, "", ""); } @@ -102,13 +105,15 @@ public class ReportDownloadPanel extends CaptionPanel { return download; } - private List<ArtifactType> getSelectedArtifacts() { - List<ArtifactType> selectedArtifacts = new ArrayList<ArtifactType>(); + private List<ReportFormatType> getSelectedReportFormats() { + List<ReportFormatType> selectedReportFormats = new ArrayList<ReportFormatType>(); if (htmlCheckBox.getValue()) - selectedArtifacts.add(HTML); + selectedReportFormats.add(HTML); if (pdfCheckBox.getValue()) - selectedArtifacts.add(PDF); - return selectedArtifacts; + selectedReportFormats.add(PDF); + if (geojsonCheckBox.getValue()) + selectedReportFormats.add(GEOJSON); + return selectedReportFormats; } } diff --git a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportUrlBuilder.java b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportUrlBuilder.java index 5f138575c7..40c3a46265 100644 --- a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportUrlBuilder.java +++ b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/client/report/ReportUrlBuilder.java @@ -23,9 +23,8 @@ package de.latlon.xplanbox.core.gwt.commons.client.report; import java.util.List; import com.google.gwt.core.client.GWT; - +import de.latlon.xplan.validator.web.shared.ReportFormatType; import de.latlon.xplanbox.core.gwt.commons.shared.ValidationSummary; -import de.latlon.xplan.validator.web.shared.ArtifactType; /** * Contains useful methods to create report urls. @@ -41,9 +40,9 @@ public class ReportUrlBuilder { return url; } - String createZipUrl(ValidationSummary validationSummary, List<ArtifactType> artifacts) { + String createZipUrl(ValidationSummary validationSummary, List<ReportFormatType> reportFormats) { String url = createBaseUrl("rest/report/zip/" + validationSummary.getPlanUuid()); - url = appendArtifacts(artifacts, url); + url = appendReportFormats(reportFormats, url); url = appendValidationName(validationSummary, url); return url; } @@ -56,18 +55,18 @@ public class ReportUrlBuilder { return url + "validationName" + "=" + validationSummary.getValidationName(); } - private String appendArtifacts(List<ArtifactType> artifacts, String url) { - return url + "artifacts" + "=" + createArtifactsAsString(artifacts) + "&"; + private String appendReportFormats(List<ReportFormatType> reportFormats, String url) { + return url + "artifacts" + "=" + createReportFormatsAsString(reportFormats) + "&"; } - private String createArtifactsAsString(List<ArtifactType> artifacts) { - StringBuilder artifactsAsString = new StringBuilder(); - for (ArtifactType artifact : artifacts) { - if (artifactsAsString.length() > 0) - artifactsAsString.append(","); - artifactsAsString.append(artifact); + private String createReportFormatsAsString(List<ReportFormatType> reportFormats) { + StringBuilder reportFormatsAsString = new StringBuilder(); + for (ReportFormatType reportFormat : reportFormats) { + if (reportFormatsAsString.length() > 0) + reportFormatsAsString.append(","); + reportFormatsAsString.append(reportFormat); } - return artifactsAsString.toString(); + return reportFormatsAsString.toString(); } } diff --git a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportController.java b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportController.java index 7a5bd7954a..2e18b18139 100644 --- a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportController.java +++ b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportController.java @@ -20,7 +20,7 @@ */ package de.latlon.xplanbox.core.gwt.commons.server.service; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,7 +70,7 @@ public class ReportController { @ResponseBody public void getZippedReport(HttpServletResponse response, @PathVariable String uuid, @RequestParam(value = "validationName", required = true) String validationName, - @RequestParam(value = "artifacts", required = true) List<ArtifactType> artifacts) throws IOException { + @RequestParam(value = "artifacts", required = true) List<ReportFormatType> artifacts) throws IOException { LOG.debug("ZIP-Report for '{}' with artifacts {} requested.", StringUtils.normalizeSpace(uuid), artifacts.stream().map(a -> a.name()).collect(Collectors.joining(","))); response.setContentType("application/zip"); diff --git a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportProvider.java b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportProvider.java index f84bf63415..ba91b2394e 100644 --- a/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportProvider.java +++ b/xplan-core/xplan-core-gwt/src/main/java/de/latlon/xplanbox/core/gwt/commons/server/service/ReportProvider.java @@ -25,7 +25,7 @@ import java.util.List; import jakarta.servlet.http.HttpServletResponse; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; /** * Provides report artefacts @@ -56,6 +56,6 @@ public interface ReportProvider { * @throws IOException */ void writeZipReport(HttpServletResponse response, String planUuid, String validationName, - List<ArtifactType> artifacts) throws IOException; + List<ReportFormatType> artifacts) throws IOException; } diff --git a/xplan-core/xplan-core-gwt/src/main/resources/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.properties b/xplan-core/xplan-core-gwt/src/main/resources/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.properties index 5ce376cafb..f783c65919 100644 --- a/xplan-core/xplan-core-gwt/src/main/resources/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.properties +++ b/xplan-core/xplan-core-gwt/src/main/resources/de/latlon/xplanbox/core/gwt/commons/client/ValidatorWebCommonsMessages.properties @@ -50,8 +50,9 @@ reportDialogTitle=Ergebnisse der Validierung reportButtonNextTitle=Weiteren Plan validieren reportButtonCloseTitle=Zur\u00FCck zu den Validierungseinstellungen reportDownloadBoxTitle=Download -reportDownloadHtml=HTML Report -reportDownloadPdf=PDF Report +reportDownloadHtml=HTML-Bericht +reportDownloadPdf=PDF-Bericht +reportDownloadGeoJson=GeoJSON reportDownloadGeometryErrors=Geometriefehler reportDownloadButtonTitle=Download reportDownloadNoArtefactsSelected=Bitte w\u00e4hlen Sie mindestens ein Artefakt aus! diff --git a/xplan-core/xplan-core-validator/pom.xml b/xplan-core/xplan-core-validator/pom.xml index deae6cf53b..0c84a2b827 100644 --- a/xplan-core/xplan-core-validator/pom.xml +++ b/xplan-core/xplan-core-validator/pom.xml @@ -73,6 +73,10 @@ <groupId>${project.groupId}</groupId> <artifactId>xplan-core-synthesizer</artifactId> </dependency> + <dependency> + <groupId>jakarta.annotation</groupId> + <artifactId>jakarta.annotation-api</artifactId> + </dependency> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> @@ -89,6 +93,11 @@ <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </dependency> + <dependency> + <groupId>io.swagger.core.v3</groupId> + <artifactId>swagger-annotations</artifactId> + <version>${swagger.version}</version> + </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/ValidationResultContext.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/ValidationResultContext.java index 4b5428ee71..936c20f0cf 100644 --- a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/ValidationResultContext.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/ValidationResultContext.java @@ -14,6 +14,7 @@ import java.util.Optional; import de.latlon.xplan.commons.feature.XPlanFeatureCollection; import de.latlon.xplan.validator.XPlanGmlValidation; import de.latlon.xplan.validator.geometric.result.GeometricValidationFinding; +import de.latlon.xplan.validator.geometric.result.GeometricValidationFindingLevel; import de.latlon.xplan.validator.geometric.result.GeometricValidationRule; import org.deegree.geometry.Geometry; @@ -74,6 +75,13 @@ public class ValidationResultContext { return planRules; } + public void add(GeometricValidationFindingLevel level, String msg, Geometry markerGeom, List<String> gmlIds) { + findings.add(new GeometricValidationFinding().message(msg) + .level(level) + .markerGeom(markerGeom) + .gmlIds(gmlIds.toArray(String[]::new))); + } + public void addError(String msg, String... gmlIds) { findings.add(new GeometricValidationFinding().message(msg).level(ERROR).gmlIds(gmlIds)); } diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/FlaechenschlussFinding.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/FlaechenschlussFinding.java new file mode 100644 index 0000000000..d254f5aee9 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/FlaechenschlussFinding.java @@ -0,0 +1,67 @@ +package de.latlon.xplan.validator.geometric.inspector.flaechenschluss; + +import static de.latlon.xplan.validator.geometric.inspector.flaechenschluss.FlaechenschlussFinding.FindingType.LUECKE_ERROR; +import static de.latlon.xplan.validator.geometric.inspector.flaechenschluss.FlaechenschlussFinding.FindingType.LUECKE_WARNING; +import static de.latlon.xplan.validator.i18n.ValidationMessages.format; +import static de.latlon.xplan.validator.i18n.ValidationMessages.getMessage; + +import java.util.List; +import java.util.stream.Collectors; + +import de.latlon.xplan.validator.geometric.result.GeometricValidationFindingLevel; +import org.deegree.geometry.Geometry; + +/** + * + * Aggregates same findings for Flaechenschlussbedingung of multiple types in one finidng. + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public record FlaechenschlussFinding(List<String> gmlIds, List<OptimisedFlaechenschlussInspector.TestStep> testSteps, + FindingType findingType, Geometry markerGeom) { + + enum FindingType { + + LUECKE_WARNING(GeometricValidationFindingLevel.WARNING), LUECKE_ERROR(GeometricValidationFindingLevel.ERROR), + INVALID_CP_WARNING(GeometricValidationFindingLevel.WARNING), + INVALID_CP_ERROR(GeometricValidationFindingLevel.ERROR); + + private final GeometricValidationFindingLevel level; + + FindingType(GeometricValidationFindingLevel level) { + this.level = level; + } + + public GeometricValidationFindingLevel getLevel() { + return level; + } + + } + + /** + * @return the message used for the aggregated findings, never <code>null</code> + */ + String createMessage() { + String conditions = testSteps.stream().map(testStep -> { + switch (testStep) { + case GELTUNGSBEREICH_BEREICH -> { + return getMessage("FlaechenschlussInspector_geltungsbereich_bereich"); + } + case GELTUNGSBEREICH_PLAN -> { + return getMessage("FlaechenschlussInspector_geltungsbereich_plan"); + } + default -> { + return getMessage("FlaechenschlussInspector_flaechenschluss"); + } + } + }).collect(Collectors.joining(", ")); + String gmlIdsAsString = gmlIds.stream().collect(Collectors.joining(", ")); + if (LUECKE_WARNING.equals(findingType)) + return format("FlaechenschlussInspector_warning_Luecke", gmlIdsAsString, conditions, markerGeom); + else if (LUECKE_ERROR.equals(findingType)) + return format("FlaechenschlussInspector_error_Luecke", gmlIdsAsString, conditions, markerGeom); + return format("FlaechenschlussInspector_error", gmlIdsAsString, conditions, markerGeom); + } + +} diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspector.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspector.java index 25cfffeab7..a6a3118182 100644 --- a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspector.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspector.java @@ -38,6 +38,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import de.latlon.xplan.commons.XPlanType; @@ -180,13 +181,15 @@ public class OptimisedFlaechenschlussInspector implements GeometricFeatureInspec private final FlaechenschlussContext flaechenschlussContext; + private final List<FlaechenschlussFinding> flaechenschlussLuecken = new ArrayList<>(); + public OptimisedFlaechenschlussInspector(XPlanVersion xPlanVersion, XPlanType xPlanType) { this.xPlanVersion = xPlanVersion; this.xPlanType = xPlanType; this.flaechenschlussContext = new FlaechenschlussContext(xPlanVersion); } - private enum TestStep { + public enum TestStep { FLAECHENSCHLUSSPAIRS, FLAECHENSCHLUSSUNION, GELTUNGSBEREICH_BEREICH, GELTUNGSBEREICH_PLAN @@ -256,6 +259,8 @@ public class OptimisedFlaechenschlussInspector implements GeometricFeatureInspec addError(msg); LOG.debug(msg, e); } + flaechenschlussLuecken.forEach(luecke -> validationResultContext.add(luecke.findingType().getLevel(), + luecke.createMessage(), luecke.markerGeom(), luecke.gmlIds())); return validationResultContext.hasNoErroneousFindings(); } @@ -453,19 +458,10 @@ public class OptimisedFlaechenschlussInspector implements GeometricFeatureInspec boolean foundInvalidControlPoints = checkControlPointsAndAddFailures(controlPointsInIntersection, testStep); if (!foundInvalidControlPoints) { boolean handleAsFailure = handleAsFailure(testStep); - String lueckeMessage = getMessage("FlaechenschlussInspector_error_Luecke"); - if (TestStep.GELTUNGSBEREICH_BEREICH.equals(testStep)) { - lueckeMessage = getMessage("FlaechenschlussInspector_error_bereich_Luecke"); - } - else if (TestStep.GELTUNGSBEREICH_PLAN.equals(testStep)) { - lueckeMessage = getMessage("FlaechenschlussInspector_error_plan_Luecke"); - } - if (handleAsFailure) { - addError(lueckeMessage, diffGeltungsbereich, gmlIds.toArray(String[]::new)); - } - else { - addWarning(lueckeMessage, diffGeltungsbereich, gmlIds.toArray(String[]::new)); - } + if (handleAsFailure) + addLuecke(gmlIds, testStep, FlaechenschlussFinding.FindingType.LUECKE_ERROR, diffGeltungsbereich); + else + addLuecke(gmlIds, testStep, FlaechenschlussFinding.FindingType.LUECKE_WARNING, diffGeltungsbereich); } } else if (relate.isDisjoint()) { @@ -499,47 +495,40 @@ public class OptimisedFlaechenschlussInspector implements GeometricFeatureInspec List<ControlPoint> controlPointsInIntersectionTmp = new ArrayList<>(controlPoints); controlPoints.forEach(cpToCheck -> { controlPointsInIntersectionTmp.remove(cpToCheck); - controlPointsInIntersectionTmp.forEach(cpInIntersection2 -> cpToCheck.checkIfIdentical(cpInIntersection2)); + controlPointsInIntersectionTmp.forEach(cpToCheck::checkIfIdentical); }); List<ControlPoint> controlPointsWithInvalidFlaechenschluss = controlPoints.stream() .filter(cp -> !cp.hasIdenticalControlPoint()) .toList(); controlPointsWithInvalidFlaechenschluss.forEach(cp -> { boolean handleAsFailure = handleAsFailure(testStep); - String msg; String gmlId = cp.getFeatureGmlId(); - if (!handleAsFailure && !TestStep.FLAECHENSCHLUSSPAIRS.equals(testStep)) { - if (TestStep.GELTUNGSBEREICH_BEREICH.equals(testStep)) { - msg = format("FlaechenschlussInspector_possibleLuecke_bereich", gmlId, cp.getPoint()); - } - else if (TestStep.GELTUNGSBEREICH_PLAN.equals(testStep)) { - msg = format("FlaechenschlussInspector_possibleLuecke_plan", gmlId, cp.getPoint()); - } - else { - msg = format("FlaechenschlussInspector_possibleLuecke", gmlId, cp.getPoint()); - } - } - else if (TestStep.GELTUNGSBEREICH_BEREICH.equals(testStep)) { - msg = format("FlaechenschlussInspector_error_bereich", gmlId, cp.getPoint()); - } - else if (TestStep.GELTUNGSBEREICH_PLAN.equals(testStep)) { - msg = format("FlaechenschlussInspector_error_plan", gmlId, cp.getPoint()); - } - else { - msg = format("FlaechenschlussInspector_error", gmlId, cp.getPoint()); - } - if (!validationResultContext.containsSameFinding(msg, cp.getPoint())) { - if (handleAsFailure) { - addError(msg, cp.getPoint(), gmlId); - } - else { - addWarning(msg, cp.getPoint(), gmlId); - } - } + if (!handleAsFailure && !TestStep.FLAECHENSCHLUSSPAIRS.equals(testStep)) + addLuecke(List.of(gmlId), testStep, FlaechenschlussFinding.FindingType.LUECKE_WARNING, cp.getPoint()); + else if (!handleAsFailure) + addLuecke(List.of(gmlId), testStep, FlaechenschlussFinding.FindingType.INVALID_CP_WARNING, + cp.getPoint()); + else + addLuecke(List.of(gmlId), testStep, FlaechenschlussFinding.FindingType.INVALID_CP_ERROR, cp.getPoint()); }); return !controlPointsWithInvalidFlaechenschluss.isEmpty(); } + private void addLuecke(List<String> gmlIds, TestStep testStep, FlaechenschlussFinding.FindingType findingType, + Geometry markerGeom) { + Optional<FlaechenschlussFinding> flaechenschlussLuecke = flaechenschlussLuecken.stream() + .filter(luecke -> gmlIds.equals(luecke.gmlIds()) && markerGeom.equals(luecke.markerGeom()) + && findingType.equals(luecke.findingType())) + .findFirst(); + if (flaechenschlussLuecke.isPresent()) + flaechenschlussLuecke.get().testSteps().add(testStep); + else { + List<TestStep> testSteps = new ArrayList<>(); + testSteps.add(testStep); + flaechenschlussLuecken.add(new FlaechenschlussFinding(gmlIds, testSteps, findingType, markerGeom)); + } + } + private void addBereichFeature(Map<BereichFeature, FeaturesUnderTest> bereichFeaturesWithFeaturesUnderTest, GeltungsbereichFeature geltungsbereichFeature, List<FeatureUnderTest> featuresUnderTest, Geometry flaechenschlussUnion) { diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/result/GeometricValidationFinding.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/result/GeometricValidationFinding.java index cd9da5971f..6071b6e173 100644 --- a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/result/GeometricValidationFinding.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/geometric/result/GeometricValidationFinding.java @@ -37,7 +37,7 @@ public class GeometricValidationFinding { return this; } - public GeometricValidationFinding gmlIds(String[] gmlIds) { + public GeometricValidationFinding gmlIds(String... gmlIds) { this.gmlIds = List.of(gmlIds); return this; } diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/ReportWriter.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/ReportWriter.java index 18eaf62c95..dab1a511ca 100644 --- a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/ReportWriter.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/ReportWriter.java @@ -8,24 +8,20 @@ * 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.xplan.validator.report; -import de.latlon.xplan.validator.report.html.HtmlReportGenerator; -import de.latlon.xplan.validator.report.pdf.PdfReportGenerator; -import de.latlon.xplan.validator.web.shared.ArtifactType; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.HTML; +import static org.apache.commons.io.IOUtils.copy; import java.io.IOException; import java.io.InputStream; @@ -38,8 +34,15 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import static de.latlon.xplan.validator.web.shared.ArtifactType.HTML; -import static org.apache.commons.io.IOUtils.copy; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.latlon.xplan.validator.report.geojson.GeoJsonBuilder; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; +import de.latlon.xplan.validator.report.html.HtmlReportGenerator; +import de.latlon.xplan.validator.report.pdf.PdfReportGenerator; +import de.latlon.xplan.validator.web.shared.ReportFormatType; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Generates an archive the {@link ValidatorReport} as XMl, HTML and PDF @@ -68,6 +71,7 @@ public class ReportWriter { List<String> failures = new ArrayList<>(); addHtmlEntry(report, targetDirectory, failures); addPdfEntry(report, targetDirectory, failures); + addGeoJsonEntry(report, targetDirectory, failures); addFailureLog(failures, targetDirectory); } @@ -76,10 +80,10 @@ public class ReportWriter { return retrieveArtifactFile(sourceDirectory, validationName, HTML); } - public void writeZipWithArtifacts(OutputStream outputStream, String validationName, List<ArtifactType> artifacts, - Path sourceDirectory) throws IOException { + public void writeZipWithArtifacts(OutputStream outputStream, String validationName, + List<ReportFormatType> artifacts, Path sourceDirectory) throws IOException { try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { - for (ArtifactType artifactType : artifacts) { + for (ReportFormatType artifactType : artifacts) { addZipEntry(artifactType, validationName, zipOutputStream, sourceDirectory); } Path errorlog = sourceDirectory.resolve(ERROR_LOG_FILENAME); @@ -111,6 +115,22 @@ public class ReportWriter { } } + private void addGeoJsonEntry(ValidatorReport report, Path directoryToCreateZip, List<String> failures) { + String validationName = report.getValidationName(); + Path geoJsonFile = directoryToCreateZip.resolve(validationName + ".geojson"); + try (OutputStream outputStream = Files.newOutputStream(geoJsonFile)) { + FeatureCollection geoJsonFailures = GeoJsonBuilder.createGeoJsonFailures(report); + if (geoJsonFailures == null) { + geoJsonFailures = new FeatureCollection(); + } + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(outputStream, geoJsonFailures); + } + catch (Exception e) { + failures.add(e.getMessage()); + } + } + private void addFailureLog(List<String> failures, Path directoryToCreateZip) { if (!failures.isEmpty()) { Path errorlog = directoryToCreateZip.resolve(ERROR_LOG_FILENAME); @@ -124,11 +144,12 @@ public class ReportWriter { } } - private void addZipEntry(ArtifactType artifactType, String validationName, ZipOutputStream zipOutputStream, + private void addZipEntry(ReportFormatType artifactType, String validationName, ZipOutputStream zipOutputStream, Path sourceDirectory) throws IOException { switch (artifactType) { case HTML: case PDF: + case GEOJSON: addSimpleArtifact(artifactType, validationName, zipOutputStream, sourceDirectory); break; default: @@ -136,8 +157,8 @@ public class ReportWriter { } } - private void addSimpleArtifact(ArtifactType artifactType, String validationName, ZipOutputStream zipOutputStream, - Path sourceDirectory) throws IOException { + private void addSimpleArtifact(ReportFormatType artifactType, String validationName, + ZipOutputStream zipOutputStream, Path sourceDirectory) throws IOException { Path artifactFile = retrieveArtifactFile(sourceDirectory, validationName, artifactType); if (Files.exists(artifactFile)) { addArtifact(zipOutputStream, artifactFile); @@ -153,7 +174,7 @@ public class ReportWriter { } } - public Path retrieveArtifactFile(Path sourceDirectory, String validationName, ArtifactType artifactType) { + public Path retrieveArtifactFile(Path sourceDirectory, String validationName, ReportFormatType artifactType) { String suffix = artifactType.name().toLowerCase(); return sourceDirectory.resolve(validationName + "." + suffix); } diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilder.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilder.java new file mode 100644 index 0000000000..4e59973b49 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilder.java @@ -0,0 +1,98 @@ +package de.latlon.xplan.validator.report.geojson; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.latlon.xplan.validator.geometric.result.GeometricValidationFinding; +import de.latlon.xplan.validator.geometric.result.GeometricValidationRule; +import de.latlon.xplan.validator.planinfo.PlanInfo; +import de.latlon.xplan.validator.report.ReportGenerationException; +import de.latlon.xplan.validator.report.ValidatorReport; +import de.latlon.xplan.validator.report.geojson.model.Feature; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; +import de.latlon.xplan.validator.report.geojson.model.Geometry; +import org.deegree.cs.coordinatesystems.ICRS; +import org.deegree.cs.exceptions.TransformationException; +import org.deegree.cs.exceptions.UnknownCRSException; +import org.deegree.cs.persistence.CRSManager; +import org.deegree.geometry.GeometryTransformer; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public final class GeoJsonBuilder { + + public static final String DEFAULT_CRS_GEOJSON = "http://www.opengis.net/def/crs/OGC/1.3/CRS84"; + + private GeoJsonBuilder() { + } + + /** + * @param report never <code>null</code> + * @return the GeoJson {@link FeatureCollection} containing all geometric ERRORS and + * WARNINGS + */ + public static FeatureCollection createGeoJsonFailures(ValidatorReport report) throws ReportGenerationException { + if (reportHasGeometricFindings(report)) { + FeatureCollection featureCollection = new FeatureCollection(); + Map<String, PlanInfo> planInfoReport = report.getPlanInfoReport().getPlanInfos(); + for (Map.Entry<String, PlanInfo> entry : planInfoReport.entrySet()) { + PlanInfo planInfo = entry.getValue(); + List<GeometricValidationRule> rules = planInfo.getGeometricValidatorResult().getRules(); + for (GeometricValidationRule rule : rules) { + if (!rule.getFindings().isEmpty()) { + for (GeometricValidationFinding finding : rule.getFindings()) { + Feature feature = createFeature(rule, finding, planInfo); + featureCollection.addFeaturesItem(feature); + } + } + } + } + return featureCollection; + } + return null; + } + + private static Feature createFeature(GeometricValidationRule rule, GeometricValidationFinding finding, + PlanInfo planInfo) throws ReportGenerationException { + Map<String, Object> properties = new HashMap<>(); + properties.put("id", rule.getId()); + properties.put("title", rule.getTitle()); + properties.put("planName", planInfo.getName()); + properties.put("level", finding.getLevel().name()); + properties.put("message", finding.getMessage()); + properties.put("gmlIds", String.join(", ", finding.getGmlIds())); + Geometry geometry = GeoJsonGeometryBuilder.createGeometry(transformToWgs84(finding.getMarkerGeom())); + return new Feature().geometry(geometry).properties(properties); + } + + private static org.deegree.geometry.Geometry transformToWgs84(org.deegree.geometry.Geometry markerGeom) + throws ReportGenerationException { + if (markerGeom == null) + return null; + try { + ICRS targetCrs = CRSManager.lookup(DEFAULT_CRS_GEOJSON); + GeometryTransformer geometryTransformer = new GeometryTransformer(targetCrs); + return geometryTransformer.transform(markerGeom); + } + catch (TransformationException | UnknownCRSException e) { + throw new ReportGenerationException(e); + } + } + + private static boolean reportHasGeometricFindings(ValidatorReport report) { + return report.getPlanInfoReport() != null && !report.getPlanInfoReport().getPlanInfos().isEmpty() + && report.getPlanInfoReport() + .getPlanInfos() + .values() + .stream() + .anyMatch(planInfo -> planInfo.getGeometricValidatorResult() != null + && planInfo.getGeometricValidatorResult() + .getRules() + .stream() + .anyMatch(rules -> !rules.getFindings().isEmpty())); + } + +} diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilder.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilder.java similarity index 76% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilder.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilder.java index ce33c6981b..b54e7a6277 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilder.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilder.java @@ -1,21 +1,21 @@ -package de.latlon.xplanbox.api.commons; +package de.latlon.xplan.validator.report.geojson; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry; -import de.latlon.xplanbox.api.commons.v2.model.geojson.GeometryCollection; -import de.latlon.xplanbox.api.commons.v2.model.geojson.GeometryElement; -import de.latlon.xplanbox.api.commons.v2.model.geojson.LineString; -import de.latlon.xplanbox.api.commons.v2.model.geojson.LineStringCoordinates; -import de.latlon.xplanbox.api.commons.v2.model.geojson.LinearRing; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiLineString; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiPoint; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiPolygon; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Point; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Polygon; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Position; +import de.latlon.xplan.validator.report.geojson.model.Geometry; +import de.latlon.xplan.validator.report.geojson.model.GeometryCollection; +import de.latlon.xplan.validator.report.geojson.model.GeometryElement; +import de.latlon.xplan.validator.report.geojson.model.LineString; +import de.latlon.xplan.validator.report.geojson.model.LineStringCoordinates; +import de.latlon.xplan.validator.report.geojson.model.LinearRing; +import de.latlon.xplan.validator.report.geojson.model.MultiLineString; +import de.latlon.xplan.validator.report.geojson.model.MultiPoint; +import de.latlon.xplan.validator.report.geojson.model.MultiPolygon; +import de.latlon.xplan.validator.report.geojson.model.Point; +import de.latlon.xplan.validator.report.geojson.model.Polygon; +import de.latlon.xplan.validator.report.geojson.model.Position; import org.deegree.geometry.composite.CompositeGeometry; import org.deegree.geometry.multi.MultiGeometry; import org.deegree.geometry.primitive.Curve; @@ -23,6 +23,7 @@ import org.deegree.geometry.primitive.GeometricPrimitive; import org.deegree.geometry.primitive.Ring; import org.deegree.geometry.primitive.Surface; import org.deegree.geometry.primitive.segments.LineStringSegment; +import org.deegree.geometry.standard.multi.DefaultMultiGeometry; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> @@ -40,6 +41,10 @@ public class GeoJsonGeometryBuilder { public static Geometry createGeometry(org.deegree.geometry.Geometry geom) { if (geom == null) return null; + return geometry(geom); + } + + private static Geometry geometry(org.deegree.geometry.Geometry geom) { switch (geom.getGeometryType()) { case PRIMITIVE_GEOMETRY -> { return geometry((GeometricPrimitive) geom); @@ -88,11 +93,27 @@ public class GeoJsonGeometryBuilder { case MULTI_POLYGON -> { return geometry((org.deegree.geometry.multi.MultiPolygon) geom); } + case MULTI_GEOMETRY -> { + return geometry((DefaultMultiGeometry<org.deegree.geometry.Geometry>) geom); + } default -> throw new IllegalArgumentException( "Could not export geometry " + geom.getMultiGeometryType() + " as GeoJSON"); } } + private static GeometryCollection geometry(DefaultMultiGeometry<org.deegree.geometry.Geometry> geom) { + GeometryCollection geometryCollection = new GeometryCollection(); + geom.forEach(g -> { + Geometry geometry = geometry(g); + if (geometry instanceof GeometryElement) + geometryCollection.addGeometriesItem((GeometryElement) geometry); + else + throw new IllegalArgumentException( + "Could not export geometry " + geom.getGeometryType() + " as GeoJSON"); + }); + return geometryCollection; + } + private static LineString geometry(Curve geom) { LineStringCoordinates coordinates = lineStringCoordinates(geom); return new LineString().coordinates(coordinates); diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Feature.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Feature.java new file mode 100644 index 0000000000..dd9215f1e9 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Feature.java @@ -0,0 +1,150 @@ +/* + * GeoJSON format + * This document defines the GeoJSON format as an OpenAPI. It contains the definitions for 'Feature' object and 'FeatureCollection' objects, as well as the definitions for all 'Geometry' objects. It conforms with the 'RFC-7946' standard from IETF (August 2016 version) Kudos to @bubbobne and @idkw whose code helped me not start from scratch https://gist.github.com/bubbobne/fe5f2db65acf039be6a9fd92fc9c7233 + * + * OpenAPI spec version: 1.0.1 + * Contact: zitoun@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package de.latlon.xplan.validator.report.geojson.model; + +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.FEATURE; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Generated; +import jakarta.validation.constraints.NotNull; + +/** + * GeoJSon 'Feature' object. Generated from OpenAPI document + * https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1 + * + * @since 8.0 + */ +@Schema(description = "GeoJSon 'Feature' object") +@Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", + date = "2024-09-09T05:36:15.967036504Z[GMT]") +public class Feature extends GeoJsonObject { + + @JsonProperty("geometry") + private Geometry geometry = null; + + @JsonProperty("properties") + private Map<String, Object> properties = new HashMap<>(); + + @JsonProperty("id") + private Object id = null; + + public Feature() { + setType(FEATURE); + } + + public Feature geometry(Geometry geometry) { + this.geometry = geometry; + return this; + } + + /** + * Get geometry + * @return geometry + **/ + @JsonProperty("geometry") + @Schema(required = true, description = "") + @NotNull + public Geometry getGeometry() { + return geometry; + } + + public void setGeometry(Geometry geometry) { + this.geometry = geometry; + } + + public Feature properties(Map<String, Object> properties) { + this.properties = properties; + return this; + } + + /** + * Get properties + * @return properties + **/ + @JsonProperty("properties") + @Schema(required = true, description = "") + public Map<String, Object> getProperties() { + return properties; + } + + public void setProperties(Map<String, Object> properties) { + this.properties = properties; + } + + public Feature id(Object id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + **/ + @JsonProperty("id") + @Schema(description = "") + @NotNull + public Object getId() { + return id; + } + + public void setId(Object id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Feature feature = (Feature) o; + return Objects.equals(this.geometry, feature.geometry) && Objects.equals(this.properties, feature.properties) + && Objects.equals(this.id, feature.id) && super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(geometry, properties, id, super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Feature {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" geometry: ").append(toIndentedString(geometry)).append("\n"); + sb.append(" properties: ").append(toIndentedString(properties)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).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(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/FeatureCollection.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/FeatureCollection.java new file mode 100644 index 0000000000..dd5c196985 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/FeatureCollection.java @@ -0,0 +1,109 @@ +/* + * GeoJSON format + * This document defines the GeoJSON format as an OpenAPI. It contains the definitions for 'Feature' object and 'FeatureCollection' objects, as well as the definitions for all 'Geometry' objects. It conforms with the 'RFC-7946' standard from IETF (August 2016 version) Kudos to @bubbobne and @idkw whose code helped me not start from scratch https://gist.github.com/bubbobne/fe5f2db65acf039be6a9fd92fc9c7233 + * + * OpenAPI spec version: 1.0.1 + * Contact: zitoun@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package de.latlon.xplan.validator.report.geojson.model; + +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.FEATURECOLLECTION; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Generated; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +/** + * GeoJSon 'FeatureCollection' object. Generated from OpenAPI document + * https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1 + * + * @since 8.0 + */ +@Schema(description = "GeoJSon 'FeatureCollection' object") +@Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", + date = "2024-09-09T05:36:15.967036504Z[GMT]") +public class FeatureCollection extends GeoJsonObject { + + @JsonProperty("features") + private List<Feature> features = new ArrayList<>(); + + public FeatureCollection() { + setType(FEATURECOLLECTION); + } + + public FeatureCollection features(List<Feature> features) { + this.features = features; + return this; + } + + public FeatureCollection addFeaturesItem(Feature featuresItem) { + this.features.add(featuresItem); + return this; + } + + /** + * Get features + * @return features + **/ + @JsonProperty("features") + @Schema(required = true, description = "") + @NotNull + @Valid + public List<Feature> getFeatures() { + return features; + } + + public void setFeatures(List<Feature> features) { + this.features = features; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FeatureCollection featureCollection = (FeatureCollection) o; + return Objects.equals(this.features, featureCollection.features) && super.equals(o); + } + + @Override + public int hashCode() { + return Objects.hash(features, super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FeatureCollection {\n"); + sb.append(" ").append(toIndentedString(super.toString())).append("\n"); + sb.append(" features: ").append(toIndentedString(features)).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(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeoJsonObject.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeoJsonObject.java new file mode 100644 index 0000000000..0981d2d726 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeoJsonObject.java @@ -0,0 +1,204 @@ +/* + * GeoJSON format + * This document defines the GeoJSON format as an OpenAPI. It contains the definitions for 'Feature' object and 'FeatureCollection' objects, as well as the definitions for all 'Geometry' objects. It conforms with the 'RFC-7946' standard from IETF (August 2016 version) Kudos to @bubbobne and @idkw whose code helped me not start from scratch https://gist.github.com/bubbobne/fe5f2db65acf039be6a9fd92fc9c7233 + * + * OpenAPI spec version: 1.0.1 + * Contact: zitoun@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package de.latlon.xplan.validator.report.geojson.model; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonValue; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Generated; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + +/** + * GeoJSon object The coordinate reference system for all GeoJSON coordinates is a + * geographic coordinate reference system, using the World Geodetic System 1984 (WGS 84) + * datum, with longitude and latitude units of decimal degrees. This is equivalent to the + * coordinate reference system identified by the Open Geospatial Consortium (OGC) URN An + * OPTIONAL third-position element SHALL be the height in meters above or below the WGS 84 + * reference ellipsoid. In the absence of elevation values, applications sensitive to + * height or depth SHOULD interpret positions as being at local ground or sea level. + * Generated from OpenAPI document + * https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1 + * + * @since 8.0 + */ +@Schema(description = "GeoJSon object The coordinate reference system for all GeoJSON coordinates is a geographic coordinate reference system, using the World Geodetic System 1984 (WGS 84) datum, with longitude and latitude units of decimal degrees. This is equivalent to the coordinate reference system identified by the Open Geospatial Consortium (OGC) URN An OPTIONAL third-position element SHALL be the height in meters above or below the WGS 84 reference ellipsoid. In the absence of elevation values, applications sensitive to height or depth SHOULD interpret positions as being at local ground or sea level. ") +@Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", + date = "2024-09-09T05:36:15.967036504Z[GMT]") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") +@JsonSubTypes({ @JsonSubTypes.Type(value = FeatureCollection.class, name = "FeatureCollection"), + @JsonSubTypes.Type(value = Feature.class, name = "Feature"), + @JsonSubTypes.Type(value = GeometryCollection.class, name = "GeometryCollection"), + @JsonSubTypes.Type(value = Point.class, name = "Point"), + @JsonSubTypes.Type(value = LineString.class, name = "LineString"), + @JsonSubTypes.Type(value = Polygon.class, name = "Polygon"), + @JsonSubTypes.Type(value = MultiPoint.class, name = "MultiPoint"), + @JsonSubTypes.Type(value = MultiLineString.class, name = "MultiLineString"), + @JsonSubTypes.Type(value = MultiPolygon.class, name = "MultiPolygon") }) +public class GeoJsonObject { + + /** + * Gets or Sets type + */ + public enum TypeEnum { + + FEATURE("Feature"), + + FEATURECOLLECTION("FeatureCollection"), + + POINT("Point"), + + MULTIPOINT("MultiPoint"), + + LINESTRING("LineString"), + + MULTILINESTRING("MultiLineString"), + + POLYGON("Polygon"), + + MULTIPOLYGON("MultiPolygon"), + + GEOMETRYCOLLECTION("GeometryCollection"); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String text) { + for (TypeEnum b : TypeEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + + } + + @JsonProperty("type") + private TypeEnum type = null; + + @JsonProperty("bbox") + private List<BigDecimal> bbox = null; + + public GeoJsonObject type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Get type + * @return type + **/ + @JsonProperty("type") + @Schema(required = true, description = "") + @NotNull + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public GeoJsonObject bbox(List<BigDecimal> bbox) { + this.bbox = bbox; + return this; + } + + public GeoJsonObject addBboxItem(BigDecimal bboxItem) { + if (this.bbox == null) { + this.bbox = new ArrayList<BigDecimal>(); + } + this.bbox.add(bboxItem); + return this; + } + + /** + * A GeoJSON object MAY have a member named \"bbox\" to include information + * on the coordinate range for its Geometries, Features, or FeatureCollections. The + * value of the bbox member MUST be an array of length 2*n where n is the number of + * dimensions represented in the contained geometries, with all axes of the most + * southwesterly point followed by all axes of the more northeasterly point. The axes + * order of a bbox follows the axes order of geometries. + * @return bbox + **/ + @JsonProperty("bbox") + @Schema(description = "A GeoJSON object MAY have a member named \"bbox\" to include information on the coordinate range for its Geometries, Features, or FeatureCollections. The value of the bbox member MUST be an array of length 2*n where n is the number of dimensions represented in the contained geometries, with all axes of the most southwesterly point followed by all axes of the more northeasterly point. The axes order of a bbox follows the axes order of geometries. ") + @NotNull + @Valid + public List<BigDecimal> getBbox() { + return bbox; + } + + public void setBbox(List<BigDecimal> bbox) { + this.bbox = bbox; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GeoJsonObject geoJsonObject = (GeoJsonObject) o; + return Objects.equals(this.type, geoJsonObject.type) && Objects.equals(this.bbox, geoJsonObject.bbox); + } + + @Override + public int hashCode() { + return Objects.hash(type, bbox); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class GeoJsonObject {\n"); + + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" bbox: ").append(toIndentedString(bbox)).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(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Geometry.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Geometry.java new file mode 100644 index 0000000000..5ee31fdb70 --- /dev/null +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Geometry.java @@ -0,0 +1,67 @@ +/* + * GeoJSON format + * This document defines the GeoJSON format as an OpenAPI. It contains the definitions for 'Feature' object and 'FeatureCollection' objects, as well as the definitions for all 'Geometry' objects. It conforms with the 'RFC-7946' standard from IETF (August 2016 version) Kudos to @bubbobne and @idkw whose code helped me not start from scratch https://gist.github.com/bubbobne/fe5f2db65acf039be6a9fd92fc9c7233 + * + * OpenAPI spec version: 1.0.1 + * Contact: zitoun@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +package de.latlon.xplan.validator.report.geojson.model; + +import java.util.Objects; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Generated; + +/** + * Abstract type for all GeoJSon object except Feature and FeatureCollection. Generated + * from OpenAPI document * https://app.swaggerhub.com/apis/OlivierMartineau/GeoJSON/1.0.1 + * + * @since 8.0 + */ +@Schema(description = "Abstract type for all GeoJSon object except Feature and FeatureCollection ") +@Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", + date = "2024-09-09T05:36:15.967036504Z[GMT]") +public class Geometry extends GeoJsonObject { + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Geometry {\n"); + sb.append(" ").append(toIndentedString(super.toString())).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(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryCollection.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryCollection.java similarity index 95% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryCollection.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryCollection.java index 25ae92f2cf..7ee2ea91c1 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryCollection.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryCollection.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.GEOMETRYCOLLECTION; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.GEOMETRYCOLLECTION; import java.util.ArrayList; import java.util.List; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryElement.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryElement.java similarity index 97% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryElement.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryElement.java index e7d3a50ceb..6eeb45113d 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/GeometryElement.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/GeometryElement.java @@ -10,7 +10,7 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; import java.util.Objects; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineString.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineString.java similarity index 94% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineString.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineString.java index 847586efe1..34ad627c37 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineString.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineString.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.LINESTRING; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.LINESTRING; import java.util.Objects; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineStringCoordinates.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineStringCoordinates.java similarity index 97% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineStringCoordinates.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineStringCoordinates.java index 8ea76b92c3..0490c82471 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LineStringCoordinates.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LineStringCoordinates.java @@ -10,7 +10,7 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; import java.util.ArrayList; import java.util.Objects; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LinearRing.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LinearRing.java similarity index 97% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LinearRing.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LinearRing.java index f8ae1da900..066baa1372 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/LinearRing.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/LinearRing.java @@ -10,7 +10,7 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; import java.util.ArrayList; import java.util.Objects; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiLineString.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiLineString.java similarity index 95% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiLineString.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiLineString.java index 5814bac574..28eeb835d1 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiLineString.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiLineString.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTILINESTRING; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTILINESTRING; import java.util.ArrayList; import java.util.List; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPoint.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPoint.java similarity index 95% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPoint.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPoint.java index 7fa22eb75e..9eb11c0a86 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPoint.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPoint.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTIPOINT; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTIPOINT; import java.util.ArrayList; import java.util.List; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPolygon.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPolygon.java similarity index 95% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPolygon.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPolygon.java index f5ca80ab10..eeaa512f53 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/MultiPolygon.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/MultiPolygon.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTIPOLYGON; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTIPOLYGON; import java.util.ArrayList; import java.util.List; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Point.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Point.java similarity index 94% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Point.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Point.java index f9e2589713..d72e161bd3 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Point.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Point.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.POINT; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.POINT; import java.util.Objects; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Polygon.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Polygon.java similarity index 95% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Polygon.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Polygon.java index 7629803525..4b3527a3e9 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Polygon.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Polygon.java @@ -10,9 +10,9 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.POLYGON; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.POLYGON; import java.util.ArrayList; import java.util.List; diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Position.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Position.java similarity index 98% rename from xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Position.java rename to xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Position.java index e3f22afb0d..6407d31725 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v2/model/geojson/Position.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/geojson/model/Position.java @@ -10,7 +10,7 @@ * Do not edit the class manually. */ -package de.latlon.xplanbox.api.commons.v2.model.geojson; +package de.latlon.xplan.validator.report.geojson.model; import java.math.BigDecimal; import java.util.ArrayList; diff --git a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/pdf/ReportBuilder.java b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/pdf/ReportBuilder.java index bbdfda3ad8..8f7ced85be 100644 --- a/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/pdf/ReportBuilder.java +++ b/xplan-core/xplan-core-validator/src/main/java/de/latlon/xplan/validator/report/pdf/ReportBuilder.java @@ -148,7 +148,7 @@ class ReportBuilder { table.addCell(createTableCell("Planname:", FONT_TEXT)); table.addCell(createTableCell(planInfo.getName(), FONT_TEXT)); - table.addCell(createTableCell("Version XPlanGML:", FONT_TEXT)); + table.addCell(createTableCell("XPlanGML-Version:", FONT_TEXT)); table.addCell(createTableCell(asLabel(planInfo.getVersion()), FONT_TEXT)); table.addCell(createTableCell("Planart:", FONT_TEXT)); String planType = planInfo.getType() != null ? planInfo.getType().name() : "-"; diff --git a/xplan-core/xplan-core-validator/src/main/resources/de/latlon/xplan/validator/i18n/validationMessages.properties b/xplan-core/xplan-core-validator/src/main/resources/de/latlon/xplan/validator/i18n/validationMessages.properties index eb3a5f30df..f993c2cf8a 100644 --- a/xplan-core/xplan-core-validator/src/main/resources/de/latlon/xplan/validator/i18n/validationMessages.properties +++ b/xplan-core/xplan-core-validator/src/main/resources/de/latlon/xplan/validator/i18n/validationMessages.properties @@ -62,21 +62,18 @@ GeltungsbereichInspector_invalid_geom=Der Geltungsbereich des Objekts mit der gm ## 2.2.1.1 - Flaechenschluss FlaechenschlussInspector_id=2.2.1.1 FlaechenschlussInspector_title=Fl\u00e4chenschlussbedingung -FlaechenschlussInspector_error=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung an folgender Stelle nicht: %s -FlaechenschlussInspector_error_plan=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung bei der Pr\u00fcfung des Geltungsbereichs des Plans an folgender Stelle nicht: %s -FlaechenschlussInspector_error_bereich=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung bei der Pr\u00fcfung des Geltungsbereichs des Bereichs an folgender Stelle nicht: %s +FlaechenschlussInspector_error=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung hinsichtlich folgender Bedingungen nicht: %s. St\u00fctzpunkte: %s FlaechenschlussInspector_error_overlapping=Das Fl\u00e4chenschlussobjekt mit der gml id %s \u00fcberdeckt das Fl\u00e4chenschlussobjekt mit der gml id %s vollst\u00e4ndig. -FlaechenschlussInspector_error_Luecke=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein L\u00fccke identifiziert. Die Geometrie mit der L\u00fccke wird in der Shape-Datei ausgegeben. -FlaechenschlussInspector_error_plan_Luecke=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein L\u00fccke bei der Pr\u00fcfung des Geltungsbereichs des Plans identifiziert. Die Geometrie mit der L\u00fccke wird in der Shape-Datei ausgegeben. -FlaechenschlussInspector_error_bereich_Luecke=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein L\u00fccke bei der Pr\u00fcfung des Geltungsbereichs des Bereichs identifiziert. Die Geometrie mit der L\u00fccke wird in der Shape-Datei ausgegeben. -FlaechenschlussInspector_error_plan_disjoint=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein Fl\u00e4chenschlussobjekt au\u00dferhalb des Geltungsbereichs des Plans identifiziert. Die Geometrie des Fl\u00e4chenschlussobjekts wird in der Shape-Datei ausgegeben. -FlaechenschlussInspector_error_bereich_disjoint=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein Fl\u00e4chenschlussobjekt au\u00dferhalb des Geltungsbereichs des Bereichs identifiziert. Die Geometrie des Fl\u00e4chenschlussobjekts wird in der Shape-Datei ausgegeben. +FlaechenschlussInspector_error_Luecke=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung hinsichtlich folgender Bedingungen nicht: %s. Es wurde ein L\u00fccke identifiziert: %s +FlaechenschlussInspector_error_plan_disjoint=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein Fl\u00e4chenschlussobjekt au\u00dferhalb des Geltungsbereichs des Plans identifiziert. +FlaechenschlussInspector_error_bereich_disjoint=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, es wurde ein Fl\u00e4chenschlussobjekt au\u00dferhalb des Geltungsbereichs des Bereichs identifiziert. FlaechenschlussInspector_error_ueberlappung=Die Fl\u00e4chenschlussbedingung ist nicht erf\u00fcllt, die beiden Fl\u00e4chenschlussobjekte mit den gml ids %s und gml id %s \u00fcberlappen sich. Schnittbereich: %s -FlaechenschlussInspector_possibleLuecke=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung an folgender Stelle nicht. Es wurde eine L\u00fccke identifiziert. Bitte pr\u00fcfen Sie, ob es sich um eine gewollte L\u00fccke handelt: %s -FlaechenschlussInspector_possibleLuecke_plan=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung bei der Pr\u00fcfung des Geltungsbereichs des Plans an folgender Stelle nicht. Es wurde eine L\u00fccke identifiziert. Bitte pr\u00fcfen Sie, ob es sich um eine gewollte L\u00fccke handelt: %s -FlaechenschlussInspector_possibleLuecke_bereich=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung bei der Pr\u00fcfung des Geltungsbereichs des Bereichs an folgender Stelle nicht. Es wurde eine L\u00fccke identifiziert. Bitte pr\u00fcfen Sie, ob es sich um eine gewollte L\u00fccke handelt: %s +FlaechenschlussInspector_warning_Luecke=Das Fl\u00e4chenschlussobjekt mit der gml id %s erf\u00fcllt die Fl\u00e4chenschlussbedingung hinsichtlich folgender Bedingungen nicht: %s. Bitte pr\u00fcfen Sie, ob es sich um eine gewollte L\u00fccke handelt: %s FlaechenschlussInspector_abort=Die Fl\u00e4chenschlusspr\u00fcfung wurde aufgrund eines unerwarteten Fehlers abgebrochen. FlaechenschlussInspector_geom_abort=Die Fl\u00e4chenschlusspr\u00fcfung wurde aufgrund von schwerwiegenden Geometriefehlern abgebrochen. +FlaechenschlussInspector_flaechenschluss=\u00dcberdeckung des Fl\u00e4chenschluss +FlaechenschlussInspector_geltungsbereich_plan=\u00dcberdeckung des Geltungsbereichs des Plans +FlaechenschlussInspector_geltungsbereich_bereich=\u00dcberdeckung des Geltungsbereichs des Bereichs ## 2.2.2.1 - geometrische Korrektheit XPlanGeometryInspector_id=2.2.2.1 XPlanGeometryInspector_title=Verwendung geometrisch korrekter Fl\u00e4chen diff --git a/xplan-core/xplan-core-validator/src/main/resources/xslt/report.xslt b/xplan-core/xplan-core-validator/src/main/resources/xslt/report.xslt index b2b0cb4fe3..fadcbfb244 100644 --- a/xplan-core/xplan-core-validator/src/main/resources/xslt/report.xslt +++ b/xplan-core/xplan-core-validator/src/main/resources/xslt/report.xslt @@ -66,12 +66,12 @@ </head> <body> <h1>Validierungsbericht</h1> - <p>Name: + <p>Validierungsname: <b> <xsl:value-of select="ValidationReport/name"/> </b> </p> - <p>XPlan Archivname: + <p>Dateiname: <b> <xsl:value-of select="ValidationReport/fileName"/> </b> @@ -93,42 +93,39 @@ <xsl:template match="Plan"> <hr/> - <h2>Validierungergebnis des Planwerkes <xsl:value-of select="Plan/name"/></h2> + <h2>Validierungergebnis für Planwerk <xsl:value-of select="Plan/name"/></h2> <p> - <b> - <p>Planname: - <b> - <xsl:value-of select="Plan/name"/> - </b> - </p> - <p>XPlanGML Version: - <b> - <xsl:value-of select="Plan/version"/> - </b> - </p> - <p>Ergebnis: - <b> - <xsl:choose> - <xsl:when test="isValid='true'"> - <font color="#00C000">valide</font> - </xsl:when> - <xsl:otherwise> - <font color="#FF0000">nicht valide</font> - </xsl:otherwise> - </xsl:choose> - </b> - </p> - <xsl:apply-templates select="ExternalReferences"/> - <xsl:apply-templates select="Validation/Sem"/> - <xsl:apply-templates select="Validation/Geom"/> - <xsl:apply-templates select="Validation/Profile"/> - </b> + <p>Planname: + <b> + <xsl:value-of select="Plan/name"/> + </b> + </p> + <p>XPlanGML-Version: + <b> + <xsl:value-of select="Plan/version"/> + </b> + </p> + <p>Validierungsergebnis: + <b> + <xsl:choose> + <xsl:when test="isValid='true'"> + <font color="#00C000">valide</font> + </xsl:when> + <xsl:otherwise> + <font color="#FF0000">nicht valide</font> + </xsl:otherwise> + </xsl:choose> + </b> + </p> + <xsl:apply-templates select="ExternalReferences"/> + <xsl:apply-templates select="Validation/Sem"/> + <xsl:apply-templates select="Validation/Geom"/> + <xsl:apply-templates select="Validation/Profile"/> </p> </xsl:template> - <xsl:template match="ValidationReport/ExternalReferences"> + <xsl:template match="ExternalReferences"> <p><h3>Externe Referenzen</h3> - <b> <xsl:choose> <xsl:when test="SkipMessage"> <xsl:value-of select="SkipMessage"/> @@ -156,11 +153,10 @@ </ul> </xsl:otherwise> </xsl:choose> - </b> </p> </xsl:template> - <xsl:template match="Validation/Sem | Validation/Geom | ValidationReport/Syn | ValidationReport/Geom"> + <xsl:template match="Validation/Sem | Validation/Geom | ValidationReport/Syn"> <p><h3> Ergebnis der <xsl:choose> @@ -215,12 +211,14 @@ <xsl:template match="RulesMetadata"> <div>Informationen zu den Regeln:</div> - <p>Version: <xsl:value-of select="version"/></p> - <p>Quelle: - <xsl:call-template name="tokenize"> - <xsl:with-param name="string" select="normalize-space(source)"/> - </xsl:call-template> - </p> + <ul style="list-style-type: none;"> + <li>Version: <xsl:value-of select="version"/></li> + <li>Quelle: + <xsl:call-template name="tokenize"> + <xsl:with-param name="string" select="normalize-space(source)"/> + </xsl:call-template> + </li> + </ul> </xsl:template> <xsl:template match="Rules"> diff --git a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspectorTest.java b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspectorTest.java index 79d90adf93..45ecfade90 100644 --- a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspectorTest.java +++ b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/geometric/inspector/flaechenschluss/OptimisedFlaechenschlussInspectorTest.java @@ -115,9 +115,9 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(false)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(37)); + assertThat(findings.size(), is(19)); assertThat(noOfErrors(findings), is(1)); - assertThat(noOfWarnings(findings), is(36)); + assertThat(noOfWarnings(findings), is(18)); GeometricValidationFinding finding = findings.get(0); assertThat(finding.getLevel(), is(ERROR)); @@ -157,8 +157,8 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(7)); - assertThat(noOfWarnings(findings), is(7)); + assertThat(findings.size(), is(4)); + assertThat(noOfWarnings(findings), is(4)); } @Test @@ -174,8 +174,8 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(7)); - assertThat(noOfWarnings(findings), is(7)); + assertThat(findings.size(), is(4)); + assertThat(noOfWarnings(findings), is(4)); } @Test @@ -188,8 +188,8 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(2)); - assertThat(noOfWarnings(findings), is(2)); + assertThat(findings.size(), is(1)); + assertThat(noOfWarnings(findings), is(1)); } @Test @@ -279,16 +279,19 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(2)); - assertThat(noOfWarnings(findings), is(2)); + assertThat(findings.size(), is(1)); + assertThat(noOfWarnings(findings), is(1)); List<String> warnings = findings.stream() .filter(f -> WARNING.equals(f.getLevel())) .map(GeometricValidationFinding::getMessage) .toList(); assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Bereichs identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); - assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Bereichs identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); + "Das Flächenschlussobjekt mit der gml id Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C, Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244, Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521, Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A erfüllt die Flächenschlussbedingung hinsichtlich folgender Bedingungen nicht: Ãœberdeckung des Geltungsbereichs des Bereichs, Ãœberdeckung des Geltungsbereichs des Plans. Bitte prüfen Sie, ob es sich um eine gewollte Lücke handelt: POLYGON ((576676.293500 5954490.416900,576956.752400 5954069.728500,576835.043800 5954053.853500,576441.057500 5953961.902400,576397.418700 5954130.402900,576676.293500 5954490.416900))")); + GeometricValidationFinding finding = findings.get(0); + assertThat(finding.getGmlIds(), + hasItems("Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C", "Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244", + "Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521", "Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A")); + assertNotNull(finding.getMarkerGeom()); } @Test @@ -302,17 +305,20 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(2)); - assertThat(noOfWarnings(findings), is(2)); + assertThat(findings.size(), is(1)); + assertThat(noOfWarnings(findings), is(1)); List<String> warnings = findings.stream() .filter(f -> WARNING.equals(f.getLevel())) .map(GeometricValidationFinding::getMessage) .toList(); assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Plans identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); - assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Bereichs identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); + "Das Flächenschlussobjekt mit der gml id Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C, Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244, Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521, Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A erfüllt die Flächenschlussbedingung hinsichtlich folgender Bedingungen nicht: Ãœberdeckung des Geltungsbereichs des Bereichs, Ãœberdeckung des Geltungsbereichs des Plans. Bitte prüfen Sie, ob es sich um eine gewollte Lücke handelt: POLYGON ((576676.293500 5954490.416900,576956.752400 5954069.728500,576835.043800 5954053.853500,576441.057500 5953961.902400,576397.418700 5954130.402900,576676.293500 5954490.416900))")); + GeometricValidationFinding finding = findings.get(0); + assertThat(finding.getGmlIds(), + hasItems("Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C", "Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244", + "Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521", "Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A")); + assertNotNull(finding.getMarkerGeom()); } @Test @@ -326,17 +332,20 @@ public class OptimisedFlaechenschlussInspectorTest { assertThat(isValid, is(true)); List<GeometricValidationFinding> findings = flaechenschlussInspector.getFindings(); - assertThat(findings.size(), is(2)); - assertThat(noOfWarnings(findings), is(2)); + assertThat(findings.size(), is(1)); + assertThat(noOfWarnings(findings), is(1)); List<String> warnings = findings.stream() .filter(f -> WARNING.equals(f.getLevel())) .map(GeometricValidationFinding::getMessage) .toList(); assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Plans identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); - assertThat(warnings, hasItem( - "Die Flächenschlussbedingung ist nicht erfüllt, es wurde ein Lücke bei der Prüfung des Geltungsbereichs des Bereichs identifiziert. Die Geometrie mit der Lücke wird in der Shape-Datei ausgegeben.")); + "Das Flächenschlussobjekt mit der gml id Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C, Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244, Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521, Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A erfüllt die Flächenschlussbedingung hinsichtlich folgender Bedingungen nicht: Ãœberdeckung des Geltungsbereichs des Bereichs, Ãœberdeckung des Geltungsbereichs des Plans. Bitte prüfen Sie, ob es sich um eine gewollte Lücke handelt: POLYGON ((576676.293500 5954490.416900,576956.752400 5954069.728500,576835.043800 5954053.853500,576441.057500 5953961.902400,576397.418700 5954130.402900,576676.293500 5954490.416900))")); + GeometricValidationFinding finding = findings.get(0); + assertThat(finding.getGmlIds(), + hasItems("Gml_3E40CD06-7F9E-4301-B244-9C93B5B9F81C", "Gml_4C6F208C-300A-4DC5-91AA-ABF379F51244", + "Gml_702A30AF-09E5-4FDD-A2D7-4924C20ED521", "Gml_E776F534-00C5-4A18-8B52-CE999F89DD2A")); + assertNotNull(finding.getMarkerGeom()); } @Test diff --git a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/ReportWriterTest.java b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/ReportWriterTest.java index e422c39246..95182cae1d 100644 --- a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/ReportWriterTest.java +++ b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/ReportWriterTest.java @@ -22,7 +22,7 @@ package de.latlon.xplan.validator.report; import static de.latlon.xplan.commons.XPlanType.BP_Plan; import static de.latlon.xplan.commons.XPlanVersion.XPLAN_60; -import static de.latlon.xplan.validator.web.shared.ArtifactType.HTML; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.HTML; import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilderTest.java b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilderTest.java new file mode 100644 index 0000000000..bb0d29041d --- /dev/null +++ b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonBuilderTest.java @@ -0,0 +1,160 @@ +package de.latlon.xplan.validator.report.geojson; + +import static de.latlon.xplan.commons.XPlanType.BP_Plan; +import static de.latlon.xplan.commons.XPlanVersion.XPLAN_52; +import static de.latlon.xplan.validator.geometric.result.GeometricValidationFindingLevel.ERROR; +import static de.latlon.xplan.validator.geometric.result.GeometricValidationFindingLevel.WARNING; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.FEATURE; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.FEATURECOLLECTION; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.POINT; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.latlon.xplan.validator.geometric.report.GeometricValidatorResult; +import de.latlon.xplan.validator.geometric.result.GeometricValidationFinding; +import de.latlon.xplan.validator.geometric.result.GeometricValidationRule; +import de.latlon.xplan.validator.planinfo.PlanInfo; +import de.latlon.xplan.validator.planinfo.PlanInfoReport; +import de.latlon.xplan.validator.report.ReportGenerationException; +import de.latlon.xplan.validator.report.ValidatorReport; +import de.latlon.xplan.validator.report.geojson.model.Feature; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; +import de.latlon.xplan.validator.report.geojson.model.Point; +import org.deegree.cs.exceptions.UnknownCRSException; +import org.deegree.cs.persistence.CRSManager; +import org.deegree.geometry.Geometry; +import org.deegree.geometry.GeometryFactory; +import org.junit.Test; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + */ +public class GeoJsonBuilderTest { + + @Test + public void test_createGeoJsonFailures_NoFindings() throws ReportGenerationException { + ValidatorReport reportWithoutFindings = createReportWithoutFindings(); + FeatureCollection geoJsonFailures = GeoJsonBuilder.createGeoJsonFailures(reportWithoutFindings); + + assertThat(geoJsonFailures, nullValue()); + } + + @Test + public void test_createGeoJsonFailures_WithFinding() throws UnknownCRSException, ReportGenerationException { + ValidatorReport reportWithoutFindings = createReportWithFinding(); + FeatureCollection geoJsonFailures = GeoJsonBuilder.createGeoJsonFailures(reportWithoutFindings); + + assertThat(geoJsonFailures, notNullValue()); + assertThat(geoJsonFailures.getType(), equalTo(FEATURECOLLECTION)); + assertThat(geoJsonFailures.getFeatures().size(), equalTo(1)); + + Feature feature = geoJsonFailures.getFeatures().get(0); + assertThat(feature.getGeometry(), notNullValue()); + assertThat(feature.getType(), equalTo(FEATURE)); + assertThat(feature.getGeometry().getType(), equalTo(POINT)); + assertThat(((Point) feature.getGeometry()).getPosition().get(0).doubleValue(), equalTo(9.991965747700812)); + assertThat(((Point) feature.getGeometry()).getPosition().get(1).doubleValue(), equalTo(53.665150839846035)); + Map<String, Object> properties = feature.getProperties(); + assertThat(properties.size(), equalTo(6)); + + PlanInfo planInfo = reportWithoutFindings.getPlanInfoReport() + .getPlanInfos() + .values() + .stream() + .findFirst() + .get(); + GeometricValidationRule rule = planInfo.getGeometricValidatorResult().getRules().get(0); + GeometricValidationFinding finding = rule.getFindings().get(0); + + assertThat(properties.get("id"), equalTo(rule.getId())); + assertThat(properties.get("title"), equalTo(rule.getTitle())); + assertThat(properties.get("planName"), equalTo(planInfo.getName())); + assertThat(properties.get("level"), equalTo(finding.getLevel().name())); + assertThat(properties.get("message"), equalTo(finding.getMessage())); + assertThat(properties.get("gmlIds"), equalTo("Gml_001, Gml_002")); + } + + @Test + public void test_createGeoJsonFailures_WithMultiplePlansAndFindings() + throws UnknownCRSException, ReportGenerationException { + ValidatorReport reportWithoutFindings = createReportWithMultiplePlansAndFindings(); + FeatureCollection geoJsonFailures = GeoJsonBuilder.createGeoJsonFailures(reportWithoutFindings); + + assertThat(geoJsonFailures, notNullValue()); + assertThat(geoJsonFailures.getFeatures().size(), equalTo(3)); + } + + private static ValidatorReport createReportWithoutFindings() { + PlanInfo planInfo = new PlanInfo("GML_plan1").name("planName").version(XPLAN_52).type(BP_Plan); + PlanInfoReport planInfoReport = new PlanInfoReport().planInfos(Collections.singletonMap("GML_plan1", planInfo)); + ValidatorReport sourceReport = new ValidatorReport(); + sourceReport.setPlanInfoReport(planInfoReport); + return sourceReport; + } + + private static ValidatorReport createReportWithFinding() throws UnknownCRSException { + GeometricValidatorResult geometricValidatorResult = createGeometricValidatorResult(); + PlanInfo planInfo = new PlanInfo("GML_plan1").name("planName") + .version(XPLAN_52) + .type(BP_Plan) + .geometricValidatorResult(geometricValidatorResult); + PlanInfoReport planInfoReport = new PlanInfoReport().planInfos(Collections.singletonMap("GML_plan1", planInfo)); + ValidatorReport sourceReport = new ValidatorReport(); + sourceReport.setPlanInfoReport(planInfoReport); + return sourceReport; + } + + private static ValidatorReport createReportWithMultiplePlansAndFindings() throws UnknownCRSException { + PlanInfo planInfo1 = new PlanInfo("GML_plan1").name("planName2") + .version(XPLAN_52) + .type(BP_Plan) + .geometricValidatorResult(createGeometricValidatorResult()); + PlanInfo planInfo2 = new PlanInfo("GML_plan2").name("planName2") + .version(XPLAN_52) + .type(BP_Plan) + .geometricValidatorResult(createGeometricValidatorResultWithErrorAndWarning()); + HashMap<String, PlanInfo> planInfos = new HashMap<>(); + planInfos.put(planInfo1.getPlanGmlId(), planInfo1); + planInfos.put(planInfo2.getPlanGmlId(), planInfo2); + PlanInfoReport planInfoReport = new PlanInfoReport().planInfos(planInfos); + ValidatorReport sourceReport = new ValidatorReport(); + sourceReport.setPlanInfoReport(planInfoReport); + return sourceReport; + } + + private static GeometricValidatorResult createGeometricValidatorResult() throws UnknownCRSException { + GeometricValidationRule rule = new GeometricValidationRule("1.1", "test1") + .addFinding(new GeometricValidationFinding().message("fehler") + .level(ERROR) + .gmlIds("Gml_001", "Gml_002") + .markerGeom(createPoint())); + return new GeometricValidatorResult(Collections.singletonList(rule)); + } + + private static GeometricValidatorResult createGeometricValidatorResultWithErrorAndWarning() + throws UnknownCRSException { + GeometricValidationRule ruleError = new GeometricValidationRule("1.1", "test1") + .addFinding(new GeometricValidationFinding().message("fehler") + .level(ERROR) + .gmlIds("Gml_001") + .markerGeom(createPoint())); + GeometricValidationRule ruleWarning = new GeometricValidationRule("1.1", "test1") + .addFinding(new GeometricValidationFinding().message("warnung") + .level(WARNING) + .gmlIds("Gml_004") + .markerGeom(createPoint())); + return new GeometricValidatorResult(List.of(ruleError, ruleWarning)); + } + + private static Geometry createPoint() throws UnknownCRSException { + return new GeometryFactory().createPoint("id", 565542.625, 5946724.305, CRSManager.lookup("EPSG:25832")); + } + +} diff --git a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilderTest.java b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilderTest.java similarity index 50% rename from xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilderTest.java rename to xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilderTest.java index f612c25b41..b244c12996 100644 --- a/xplan-core/xplan-core-api/src/test/java/de/latlon/xplanbox/api/commons/GeoJsonGeometryBuilderTest.java +++ b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/geojson/GeoJsonGeometryBuilderTest.java @@ -1,39 +1,37 @@ -package de.latlon.xplanbox.api.commons; - -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.GEOMETRYCOLLECTION; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.LINESTRING; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTILINESTRING; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTIPOINT; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.MULTIPOLYGON; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.POINT; -import static de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry.TypeEnum.POLYGON; -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; +package de.latlon.xplan.validator.report.geojson; + +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.GEOMETRYCOLLECTION; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.LINESTRING; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTILINESTRING; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTIPOINT; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.MULTIPOLYGON; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.POINT; +import static de.latlon.xplan.validator.report.geojson.model.GeoJsonObject.TypeEnum.POLYGON; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + import java.util.Collections; import java.util.List; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import de.latlon.xplanbox.api.commons.v2.model.ValidationReport; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Geometry; -import de.latlon.xplanbox.api.commons.v2.model.geojson.GeometryCollection; -import de.latlon.xplanbox.api.commons.v2.model.geojson.LineString; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiLineString; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiPoint; -import de.latlon.xplanbox.api.commons.v2.model.geojson.MultiPolygon; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Point; -import de.latlon.xplanbox.api.commons.v2.model.geojson.Polygon; +import de.latlon.xplan.validator.report.geojson.model.Geometry; +import de.latlon.xplan.validator.report.geojson.model.GeometryCollection; +import de.latlon.xplan.validator.report.geojson.model.LineString; +import de.latlon.xplan.validator.report.geojson.model.MultiLineString; +import de.latlon.xplan.validator.report.geojson.model.MultiPoint; +import de.latlon.xplan.validator.report.geojson.model.MultiPolygon; +import de.latlon.xplan.validator.report.geojson.model.Point; +import de.latlon.xplan.validator.report.geojson.model.Polygon; import org.deegree.cs.coordinatesystems.ICRS; import org.deegree.cs.exceptions.UnknownCRSException; import org.deegree.cs.persistence.CRSManager; import org.deegree.geometry.GeometryFactory; import org.deegree.geometry.composite.CompositeGeometry; +import org.deegree.geometry.multi.MultiGeometry; import org.deegree.geometry.points.Points; import org.deegree.geometry.primitive.GeometricPrimitive; import org.deegree.geometry.primitive.Ring; -import org.junit.jupiter.api.Test; +import org.junit.Test; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> @@ -43,100 +41,125 @@ public class GeoJsonGeometryBuilderTest { private final GeometryFactory geometryFactory = new GeometryFactory(); @Test - void test_Point() throws UnknownCRSException { + public void test_Point() throws UnknownCRSException { org.deegree.geometry.primitive.Point point = createPoint(5.4, 10.8); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(point); - assertThat(geometry).isInstanceOf(Point.class); - assertThat(geometry.getType()).isEqualTo(POINT); - assertThat(((Point) geometry).getPosition().get(0).doubleValue()).isEqualTo(point.get0()); - assertThat(((Point) geometry).getPosition().get(1).doubleValue()).isEqualTo(point.get1()); + assertThat(geometry, instanceOf(Point.class)); + assertThat(geometry.getType(), is(POINT)); + assertThat(((Point) geometry).getPosition().get(0).doubleValue(), is(point.get0())); + assertThat(((Point) geometry).getPosition().get(1).doubleValue(), is(point.get1())); } @Test - void test_MultiPoint() throws UnknownCRSException { + public void test_MultiPoint() throws UnknownCRSException { org.deegree.geometry.primitive.Point point1 = createPoint(5.4, 10.8); org.deegree.geometry.primitive.Point point2 = createPoint(6.4, 10.5); org.deegree.geometry.multi.MultiPoint multiPoint = geometryFactory.createMultiPoint("id", crs(), List.of(point1, point2)); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(multiPoint); - assertThat(geometry).isInstanceOf(MultiPoint.class); - assertThat(geometry.getType()).isEqualTo(MULTIPOINT); - assertThat(((MultiPoint) geometry).getPositions().size()).isEqualTo(2); - assertThat(((MultiPoint) geometry).getPositions().get(0).get(0).doubleValue()).isEqualTo(point1.get0()); - assertThat(((MultiPoint) geometry).getPositions().get(0).get(1).doubleValue()).isEqualTo(point1.get1()); + assertThat(geometry, instanceOf(MultiPoint.class)); + assertThat(geometry.getType(), is(MULTIPOINT)); + assertThat(((MultiPoint) geometry).getPositions().size(), is(2)); + assertThat(((MultiPoint) geometry).getPositions().get(0).get(0).doubleValue(), is(point1.get0())); + assertThat(((MultiPoint) geometry).getPositions().get(0).get(1).doubleValue(), is(point1.get1())); } @Test - void test_LineString() throws UnknownCRSException { + public void test_LineString() throws UnknownCRSException { org.deegree.geometry.primitive.LineString lineString = createLineString1(); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(lineString); - assertThat(geometry).isInstanceOf(LineString.class); - assertThat(geometry.getType()).isEqualTo(LINESTRING); - assertThat(((LineString) geometry).getCoordinates().size()).isEqualTo(3); - assertThat(((LineString) geometry).getCoordinates().get(0).get(0).doubleValue()) - .isEqualTo(lineString.getControlPoints().get(0).get0()); - assertThat(((LineString) geometry).getCoordinates().get(0).get(1).doubleValue()) - .isEqualTo(lineString.getControlPoints().get(0).get1()); + assertThat(geometry, instanceOf(LineString.class)); + assertThat(geometry.getType(), is(LINESTRING)); + assertThat(((LineString) geometry).getCoordinates().size(), is(3)); + assertThat(((LineString) geometry).getCoordinates().get(0).get(0).doubleValue(), + is(lineString.getControlPoints().get(0).get0())); + assertThat(((LineString) geometry).getCoordinates().get(0).get(1).doubleValue(), + is(lineString.getControlPoints().get(0).get1())); } @Test - void test_MultiLineString() throws UnknownCRSException { + public void test_MultiLineString() throws UnknownCRSException { org.deegree.geometry.primitive.LineString lineString1 = createLineString1(); org.deegree.geometry.primitive.LineString lineString2 = createLineString2(); org.deegree.geometry.multi.MultiLineString multiLineString = geometryFactory.createMultiLineString("id", crs(), List.of(lineString1, lineString2)); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(multiLineString); - assertThat(geometry).isInstanceOf(MultiLineString.class); - assertThat(geometry.getType()).isEqualTo(MULTILINESTRING); - assertThat(((MultiLineString) geometry).getLineStringCoordinates().size()).isEqualTo(2); - assertThat(((MultiLineString) geometry).getLineStringCoordinates().get(0).get(0).get(0).doubleValue()) - .isEqualTo(multiLineString.get(0).getControlPoints().get(0).get0()); - assertThat(((MultiLineString) geometry).getLineStringCoordinates().get(0).get(0).get(1).doubleValue()) - .isEqualTo(multiLineString.get(0).getControlPoints().get(0).get1()); + assertThat(geometry, instanceOf(MultiLineString.class)); + assertThat(geometry.getType(), is(MULTILINESTRING)); + assertThat(((MultiLineString) geometry).getLineStringCoordinates().size(), is(2)); + assertThat(((MultiLineString) geometry).getLineStringCoordinates().get(0).get(0).get(0).doubleValue(), + is(multiLineString.get(0).getControlPoints().get(0).get0())); + assertThat(((MultiLineString) geometry).getLineStringCoordinates().get(0).get(0).get(1).doubleValue(), + is(multiLineString.get(0).getControlPoints().get(0).get1())); } @Test - void test_Polygon() throws UnknownCRSException { + public void test_Polygon() throws UnknownCRSException { org.deegree.geometry.primitive.Polygon polygon = createPolygon1(); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(polygon); - assertThat(geometry).isInstanceOf(Polygon.class); - assertThat(geometry.getType()).isEqualTo(POLYGON); - assertThat(((Polygon) geometry).getLinearRings().size()).isEqualTo(1); - assertThat(((Polygon) geometry).getLinearRings().get(0).size()).isEqualTo(4); - - assertThat(((Polygon) geometry).getLinearRings().get(0).get(0).get(0).doubleValue()) - .isEqualTo(polygon.getExteriorRing().getControlPoints().get(0).get0()); - assertThat(((Polygon) geometry).getLinearRings().get(0).get(0).get(1).doubleValue()) - .isEqualTo(polygon.getExteriorRing().getControlPoints().get(0).get1()); + assertThat(geometry, instanceOf(Polygon.class)); + assertThat(geometry.getType(), is(POLYGON)); + assertThat(((Polygon) geometry).getLinearRings().size(), is(1)); + assertThat(((Polygon) geometry).getLinearRings().get(0).size(), is(4)); + + assertThat(((Polygon) geometry).getLinearRings().get(0).get(0).get(0).doubleValue(), + is(polygon.getExteriorRing().getControlPoints().get(0).get0())); + assertThat(((Polygon) geometry).getLinearRings().get(0).get(0).get(1).doubleValue(), + is(polygon.getExteriorRing().getControlPoints().get(0).get1())); } @Test - void test_MultiPolygon() throws UnknownCRSException { + public void test_MultiPolygon() throws UnknownCRSException { org.deegree.geometry.primitive.Polygon polygon1 = createPolygon1(); org.deegree.geometry.primitive.Polygon polygon2 = createPolygon1(); org.deegree.geometry.multi.MultiPolygon multiPolygon = geometryFactory.createMultiPolygon("id", crs(), List.of(polygon1, polygon2)); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(multiPolygon); - assertThat(geometry).isInstanceOf(MultiPolygon.class); - assertThat(geometry.getType()).isEqualTo(MULTIPOLYGON); - assertThat(((MultiPolygon) geometry).getLinearRings().size()).isEqualTo(2); - assertThat(((MultiPolygon) geometry).getLinearRings().get(0).size()).isEqualTo(1); - assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).size()).isEqualTo(4); - - assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).get(0).get(0).doubleValue()) - .isEqualTo(multiPolygon.get(0).getExteriorRing().getControlPoints().get(0).get0()); - assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).get(0).get(1).doubleValue()) - .isEqualTo(multiPolygon.get(0).getExteriorRing().getControlPoints().get(0).get1()); + assertThat(geometry, instanceOf(MultiPolygon.class)); + assertThat(geometry.getType(), is(MULTIPOLYGON)); + assertThat(((MultiPolygon) geometry).getLinearRings().size(), is(2)); + assertThat(((MultiPolygon) geometry).getLinearRings().get(0).size(), is(1)); + assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).size(), is(4)); + + assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).get(0).get(0).doubleValue(), + is(multiPolygon.get(0).getExteriorRing().getControlPoints().get(0).get0())); + assertThat(((MultiPolygon) geometry).getLinearRings().get(0).get(0).get(0).get(1).doubleValue(), + is(multiPolygon.get(0).getExteriorRing().getControlPoints().get(0).get1())); } @Test - void test_GeometryCollection() throws UnknownCRSException { + public void test_MultiGeometry() throws UnknownCRSException { + org.deegree.geometry.primitive.Point point1 = createPoint(5.4, 10.8); + org.deegree.geometry.primitive.LineString lineString1 = createLineString1(); + org.deegree.geometry.primitive.Polygon polygon1 = createPolygon1(); + MultiGeometry<org.deegree.geometry.Geometry> multiGeometry = geometryFactory.createMultiGeometry("id", crs(), + List.of(point1, lineString1, polygon1)); + + Geometry geometry = GeoJsonGeometryBuilder.createGeometry(multiGeometry); + assertThat(geometry, instanceOf(GeometryCollection.class)); + assertThat(geometry.getType(), is(GEOMETRYCOLLECTION)); + assertThat(((GeometryCollection) geometry).getGeometries().size(), is(3)); + assertThat(((GeometryCollection) geometry).getGeometries().get(0), instanceOf(Point.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(0).getType(), is(POINT)); + assertThat(((GeometryCollection) geometry).getGeometries().get(1), instanceOf(LineString.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(1).getType(), is(LINESTRING)); + assertThat(((GeometryCollection) geometry).getGeometries().get(2), instanceOf(Polygon.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(2).getType(), is(POLYGON)); + + assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(0).doubleValue(), + is(point1.get0())); + assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(1).doubleValue(), + is(point1.get1())); + } + + @Test + public void test_GeometryCollection() throws UnknownCRSException { org.deegree.geometry.primitive.Point point1 = createPoint(5.4, 10.8); org.deegree.geometry.primitive.LineString lineString1 = createLineString1(); org.deegree.geometry.primitive.Polygon polygon1 = createPolygon1(); @@ -144,20 +167,20 @@ public class GeoJsonGeometryBuilderTest { List.of(point1, lineString1, polygon1)); Geometry geometry = GeoJsonGeometryBuilder.createGeometry(compositeGeometry); - assertThat(geometry).isInstanceOf(GeometryCollection.class); - assertThat(geometry.getType()).isEqualTo(GEOMETRYCOLLECTION); - assertThat(((GeometryCollection) geometry).getGeometries().size()).isEqualTo(3); - assertThat(((GeometryCollection) geometry).getGeometries().get(0)).isInstanceOf(Point.class); - assertThat(((GeometryCollection) geometry).getGeometries().get(0).getType()).isEqualTo(POINT); - assertThat(((GeometryCollection) geometry).getGeometries().get(1)).isInstanceOf(LineString.class); - assertThat(((GeometryCollection) geometry).getGeometries().get(1).getType()).isEqualTo(LINESTRING); - assertThat(((GeometryCollection) geometry).getGeometries().get(2)).isInstanceOf(Polygon.class); - assertThat(((GeometryCollection) geometry).getGeometries().get(2).getType()).isEqualTo(POLYGON); - - assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(0).doubleValue()) - .isEqualTo(point1.get0()); - assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(1).doubleValue()) - .isEqualTo(point1.get1()); + assertThat(geometry, instanceOf(GeometryCollection.class)); + assertThat(geometry.getType(), is(GEOMETRYCOLLECTION)); + assertThat(((GeometryCollection) geometry).getGeometries().size(), is(3)); + assertThat(((GeometryCollection) geometry).getGeometries().get(0), instanceOf(Point.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(0).getType(), is(POINT)); + assertThat(((GeometryCollection) geometry).getGeometries().get(1), instanceOf(LineString.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(1).getType(), is(LINESTRING)); + assertThat(((GeometryCollection) geometry).getGeometries().get(2), instanceOf(Polygon.class)); + assertThat(((GeometryCollection) geometry).getGeometries().get(2).getType(), is(POLYGON)); + + assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(0).doubleValue(), + is(point1.get0())); + assertThat(((Point) ((GeometryCollection) geometry).getGeometries().get(0)).getPosition().get(1).doubleValue(), + is(point1.get1())); } private org.deegree.geometry.primitive.LineString createLineString1() throws UnknownCRSException { @@ -190,20 +213,6 @@ public class GeoJsonGeometryBuilderTest { return CRSManager.lookup("EPSG:4326"); } - private static void serialize(Geometry geometry) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectMapper objectMapper = new ObjectMapperContextResolver().getContext(ValidationReport.class); - objectMapper.registerModule(new JavaTimeModule()); - try { - objectMapper.writerWithDefaultPrettyPrinter().writeValue(bos, geometry); - String serializedReport = bos.toString(); - System.out.println(serializedReport); - } - catch (IOException e) { - throw new RuntimeException(e); - } - } - private org.deegree.geometry.primitive.Point createPoint(double x, double y) throws UnknownCRSException { return geometryFactory.createPoint("id", x, y, crs()); } diff --git a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/html/HtmlReportGeneratorTest.java b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/html/HtmlReportGeneratorTest.java index 58d6a0aef6..aeaa57edda 100644 --- a/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/html/HtmlReportGeneratorTest.java +++ b/xplan-core/xplan-core-validator/src/test/java/de/latlon/xplan/validator/report/html/HtmlReportGeneratorTest.java @@ -70,13 +70,13 @@ public class HtmlReportGeneratorTest { htmlReportGenerator.generateHtmlReport(createValidatorReportWithSemanticFailures(), html); - assertThat(html.toString(), hasXPath("/html/body/p[4]/b/p[4]", containsString("semantischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[4]/p[4]", containsString("semantischen"))); assertThat(html.toString(), - hasXPath("/html/body/p[4]/b/p[4]/p/ul/li[1]", containsString("2 Validierungsregeln"))); + hasXPath("/html/body/p[4]/p[4]/p/ul/li[1]", containsString("2 Validierungsregeln"))); assertThat(html.toString(), - hasXPath("/html/body/p[4]/b/p[4]/p/ul/li[2]", containsString("1 Validierungsregeln nicht"))); + hasXPath("/html/body/p[4]/p[4]/p/ul/li[2]", containsString("1 Validierungsregeln nicht"))); assertThat(html.toString(), - hasXPath("/html/body/p[4]/b/p[4]/p/ul/li[3]", containsString("1 Validierungsregeln"))); + hasXPath("/html/body/p[4]/p[4]/p/ul/li[3]", containsString("1 Validierungsregeln"))); } @Test @@ -96,8 +96,8 @@ public class HtmlReportGeneratorTest { htmlReportGenerator.generateHtmlReport(createValidatorReportWithGeometricWarnings(), html); - assertThat(html.toString(), hasXPath("/html/body/p[4]/b/p[4]", containsString("geometrischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[4]/b/p[4]/p[2]", containsString("1 Warnungen"))); + assertThat(html.toString(), hasXPath("/html/body/p[4]/p[4]", containsString("geometrischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[4]/p[4]/p[2]", containsString("1 Warnungen"))); } @Test @@ -108,8 +108,8 @@ public class HtmlReportGeneratorTest { htmlReportGenerator.generateHtmlReport(createValidatorReportWithAllTypes(), html); assertThat(html.toString(), hasXPath("/html/body/p[4]", containsString("syntaktischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[5]/b/p[4]", containsString("semantischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[5]/b/p[5]", containsString("geometrischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[5]/p[4]", containsString("semantischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[5]/p[5]", containsString("geometrischen"))); } @Test @@ -120,9 +120,9 @@ public class HtmlReportGeneratorTest { htmlReportGenerator.generateHtmlReport(createValidatorReportWithAllTypesAndProfile(), html); assertThat(html.toString(), hasXPath("/html/body/p[4]", containsString("syntaktischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[5]/b/p[4]", containsString("semantischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[5]/b/p[5]", containsString("geometrischen"))); - assertThat(html.toString(), hasXPath("/html/body/p[5]/b/p[6]", containsString("gegen das Profil"))); + assertThat(html.toString(), hasXPath("/html/body/p[5]/p[4]", containsString("semantischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[5]/p[5]", containsString("geometrischen"))); + assertThat(html.toString(), hasXPath("/html/body/p[5]/p[6]", containsString("gegen das Profil"))); } @Test(expected = IllegalArgumentException.class) diff --git a/xplan-database/xplan-database-docker/Dockerfile b/xplan-database/xplan-database-docker/Dockerfile index e76863d0c0..26e9779a11 100644 --- a/xplan-database/xplan-database-docker/Dockerfile +++ b/xplan-database/xplan-database-docker/Dockerfile @@ -1,5 +1,5 @@ -# latest postgis/postgis:14-3.4 on 18.09.24 -FROM postgis/postgis@sha256:5dd86178d215734186c28a1d28f44b8bea08798441faab06eb2deabb778d898d +# latest postgis/postgis:14-3.4 on 24.09.24 +FROM postgis/postgis@sha256:70e61250ca30ae0b18bebf1a528b026e39f99a55d276f3daa5eac3a7ed15f3e9 ARG BUILD_DATE=? ARG DOCKER_IMAGE_NAME=? diff --git a/xplan-documentation/xplan-benutzerhandbuch/src/main/asciidoc/images/validator/Validierungsbericht_download.png b/xplan-documentation/xplan-benutzerhandbuch/src/main/asciidoc/images/validator/Validierungsbericht_download.png index 13d1a8a825fc142c43f19483291f46f758126b4d..599ae3a19a5458632fe83304c9f484486238bbfa 100644 GIT binary patch literal 28244 zcmeFYWmr|;-ZcyeQsO408>A$aj!mbufHVjSNOyNgNDE4LcStt^f`F8CcXv0>-2Trw z_j%v<y57&vhns6{_TFo+xnjm|jxl~?hA1gWVW1JA!NI{{$ViL7frEoT1+RRR$6!x+ zB0hL~B5W!qrX(XKMxkV9ZER{`1P3P_@>LyKL**A<s`i&C;g9f$*#Yy2SPbyl0Yjn( z@~WcP6tM)4M7knaUeq~N_+aSbDwmHi(Hoj5?|N3Grx%AJH6GI<In1fIa4&E>-`@-- zJS6iv@2Bv?<)<tLi`VD#!MXlGu$7A<hcfVfQrL$7kd9R4%W1P{b0X27pU(!@r*w01 zy#H7@dr!^1U|i_t!F+h}=%gNwLK!pf<hb4ylN<rAZ1r;|5*%hu)6^(J^s^4Ms!lfH zV0;<dToU!ywhbf)F;ksg<rG$rgk?T;V3{DKyAd9ridxZ*D(wYLD)-=^z$;~m?WV+W zCm`Drb&d~7B37uTMc?}?tS@l<3F72U?l)jc3oWL<LO+(MPu_Yk;k$SK)%W$uRe69K zjVuWXbqag`OiA*uj5oyywrOloDr`gfU?CKjSMI^eufIL#(X)yqvN_RYGZ98Mlp^aE zdS>`2_|d0&ECVz_s2FBXAVDdnLt?rQDRKo%XXxb>d<9w(^YX8kX!;*L1LM0?4>x!W z6Z8wpkiR6m<cdBx;-!@gb*qL}E@M;X3R~#WO9%UC;2H+qCQmAT`dydF^tLl9Tmzdr ze1T+{poOhRu{09bIQY@~XXiPtS7Ik!H6KtXvCuryDHvHaQpt<M`=Yxb8-BX$=m|$+ ze7CpR<r(#dm-q0;_3xn`1|G$t8~*uitdfFwdBJvB&(7-@gt(=9lSFru^$CgyLg}8h zAu&vJvx_4s+aB{AWm+gm#|n^n@S;3oT7+y0Bbf8WKdJOh&nnA+%>QynfJY7%p<R6B zj>OJEr94g@FrlG@cpnf9IS%T+`sH?&j(Myfq<J+)_MyiO-H6(*O(pn5n7-%J1@;vE zQh6=nA>u1PZ<md>-cLvCKVIxTT-;*bq!@_O%C$=e3XF^eGKIj4dmFO7jQZ%mvoc>t zNA|d^rJQf+$*ku`vV}cVMbwl4seqjTmLGdEeuhVi%7OkY`Q-UM64#OSZOj?D==(wL zB98fCJB!zCWX^$J@pX{XT?EZ<R92>MH-$_eEFTLAV0<E-iVpiC73KMBTqshtTFsI0 zZS?we_ifL6dGY~n^STX_hhp-|L4V85!EcjPyA)1%{2?jb5r!B(5!LU)HTa$6J_o5! z;7n~39ph1l(jjl7+%H*g{(8-xaG<d=xpOHouilnMdnfs5{KK$A1qFMN&p-e_LfQbH z9jWAS3Q0;beAGVc@!&+#$Eg<I%_{GW^#^z#^}fZ$#pnYaX>_=oxy7A@)0FSVZ6#ua zKKAlDA$3XvOC#v;UNobNNx8^jK7_NayS_L!NU3f3h(4H@sLR5y;!&~&{dRPo<oYGN zBcq2mddI8rh|A~qJK{;CbH7LF@FYld?SwX1I*%vXb?e+3+R=v~^S*;9eo>e&M8!@q z-*+NZh$Uk_UJgg3Fpz%oB`ljVAs927f<5|5ny?ak%Ev;YF#1j!#}1n}(t)ZrI!K!J znCL3NFdwb(6H(^3&5yCZuZER4o~HO1XQ~WQIlwnO(+zr?;XdSc`rHkxIl?J}bqM~+ zyWaSx#(q=ppM-sqFl5kpO#9RLeU1k2+fQSLzYlr!yBf*`cc~RRO?gObgBW=TBD?D^ zpLuY#ATIbE{Mb3-x_xnvdC=X8m5=zykF?#tjrKX#D^wL!ngF{WPk$KvFui1P#F@tm z5hu;W9mZ%0{ncr){A3w>S<;a72(cpOGM-16D*9B~={f70=OJP*D85FG$DBuLNvliG zWkX)~zW(`I;kEB;hS#DwhB<aQbFaI<meNC2uI1j+X-7eOqCb$i^0^Ya^4BMLgv`p{ z%MGV%C^ymD##;0W^zyxCUgKCJTMMtOtSqQ(w5zHVb}c>TTl4JQh$VmFtvak)p=MB! zT_~;kt#DVBH^WYQ4dd(U>g?+`D)~sGIiqIV4BL;kk<}6l{a<K*&Eu1*7OmDh`@Aj7 zS(*5WNX0T!D_X05TBKUm%yavLqpagMJ}J){w@+>{*YaoZ7>Pd#!llCn!fA*aIS0&7 zKYrn0=6uBAnuyL(YnnRZA=mNIm;*0dHc4?tfnR=SoXSkow4%DQy0_}w%<<!yxw#p? zIo(IaKcz#0xpO)GTN>Ntj&_dUwn&ERGfqqcmQdDF?t{Ci&7^b)kMx|gOUqdFYgKC% zE*kdfpbdIHdJ*$z&5xUJuIpM#ysGY1@8qxZ&Tb^l!cR$lk!tcCxApKp&i5@532T4i zZ~1ud@uNV=K#@SIj<dkLCpTdZVIJX?;ctmaxwkoU3Ehd&!_<ju354>@|0JC1T=TgR zH!pOyJ)aaetQ^bkSp8n`)wId9!Zc;ell?Pc2UCYdwPulKYkh@l-Id%4(Fs4^2lPOR zZ(UlU-|-HZOPFw&@wK=#&Na5QDoQ>Vn-%YCs+4#Z=V_>G>VK!4X0x!f;F{K{k+9UY z_-?^y!8(=URO*D#819skg_1QZg({^M-e6j0I9SD6RaW7>nZAO#uCzO}cxYF3wHZ6i zD49lj_j|bO<%DhH%G}Gu_o6?aMVgYGO#d1W*->3`uw-(ySe)d2Pu$&NS7X1<Z*AAI zCTfrBthqU|eLObyTWwcsXL`qY|J(Z8EvJ}_u3$qZgJgTUx|{kzTcPdy;f}GawiTal z{nYbR-qc2@`%lgAmB<;v{ff8({BH9i^*HW1{@?DQ&T)|;knX(Rjp(uj)<Nf>1NK}D z_N4lttC7KB#a-vg18AQ=XQ<;3@)O80>)8Ry(34=uB<3N-5C<E3Ic>R656wr(p^$f? zHXjc^FD6-tamDHW-iO$tC*Y3Y6=D++H&C7`$6Lnp6!Vf`Kv4B@y;y%xi4qKBqEbyo z7f~HZX-de4@L|h`MSLy|_99}ZH8$>_($Lz?o}8;VSiW8U)-@Mi8xhx8OPf!VO{+@R zq<E^RlQ8*}gU|HsNY{tE4<dH&(HLkbjih?pE`5&7e>&$iWPi(^kAKH$FdcA$b4ItW zAiyPKiET2{&LOI0IK5xRnk=n=YeJEdqL(l~Xk<HKr+?_-TIAa7!FvDt{`-@%u&;#B z1igf!SMJ|EDto_QBQmu=ZZFzh-2FXuU{7W5wEkqBc71$}VJ&Bk6P5E5?~j^ak}-Mm z`_HCAUW@Ch2;>$gjIch%J?N1-rd#F4``Au?<rGrAivO!a^x((m9}pFsT(accm-0L% zo(+4aU4PPWk?`)g9;LOX4D<c@u-=paF?nfXnG)WwkT%EHZ86}-J7@2|zMm+S5XXLS zVc5O=waa>gZopdaUERBVzwTDVyEiJCT2w6c^+Wbc>k^k#?U@l>@A1p@Z%bm;-7@+s ziko)(u=l^9w?<q!&XxJgvVU`8ve&IK<+ZK1xeCV>WOOPS)zGXuJ}Nv^J1g2Ml2vcj zWjFnOWw&ws+nm9i#=LR-Q;E5bb&J>4H7ju?mrmJXQGO9ezK+h77q#`uSNWDCGj3ul zmYG}A(06?$B_i58MNCD#wOF;dR_+Z@gPW-Zf>yPIp#|ldl^unpfl}R<jnjp5g+<Q9 zyLxZ^$ED5OF0%LQn<l)i-<Npqjq{~6RC>Nk`>=3wXL@5zf&v|Ki?S_0kLt?k?WpR= zIg`FSy}l1D4lMn-`g0&RDtFv?aJ@r{Bw|;<%&uxV{=CAdo1q(@gxrF`jF5+tZ`k?O z$?D3uOo_81O-jQ!-rTq?-|1;RyRV5ticzX1?}p?4rfcMhJU#(F5le$!v9+_Q&~0Pi zK@h(V|2AvAu36KrSAD&`&c+d=pt^$IS*zPR(am(%4%P&}b>~8zBm0^01oM1J>P&sD z{v1Qo$<_X>dw_e+#n?fd5SxqAS<r8CN8M80ZnA433-4z)oBQwgSMU{&(>%8H4r8yY zZWE5aZ<FPvW;OdZ$E8Jj&)<ojF5Bz>N+a@q+fwD_y2-lGI(-v!-KZa;kI|xcEP3#I zpmxAo8#(haSyP|;#g*q4ax<h+@ReZdUF_x7)JB26wEnnf_0{w(^XW`;dsDjzmQ;WA z9ooC{OhQ7qrg>C2PC_^$fi&fxeS_YWem#T5uMqEXzNKLqb>aGGT|SV_AwO7IA(UJ5 z3wVimR;$8g=EHT?KXTZeT;g+h8<WDA^9f}TUOTfTcO%E&TV>T`fPDGB9_fvm$)WuX z>(r)4>{OTcET9yhjWlG8<>ldCfX^s!i10*kNZ=DZcnQK2|L3zLJU!f_zxxs3;DSuy z5dU?I0(gi0MS&Nr%s=msz68P{gKya2<&uu@@1x;Q(;xlkb3XVDPDDjaMh3jAytgwl zva<hZ?O@eXr4DwW+DL2K!@=Rvz+Uh&Z=N55^N*XVYB*@f%kjOpwq!B*U~Oo`;$mq7 zI}e<I3m^DsY2;u);bLiFWzXjVrTTjWANUO0%}Pb__YenjD3yl15`~zxoe>2W3mXd? zl^_}g1%-g!2V=fB;*$R=4!%LDJ~}wq@UgNwJ3F&Dv$I&+nXta(<>h5%d&T<d6*D-3 z+1}O4!N7&t%AWe4ll=QU;zsuG?M!VPOs%abVCOY3w03lWQc=M!^q;?f?$gM{^nb2o zW&f|o0uRUv`-Sx-3mfZy&JBtR!1nSfnYtKRXo{O!f;t1&5af8pA@KM3|IaV~bH)EE zsqsH0Ioa6$yX1fU^8YTWW^ZICW^D;B=^*$&&GoOs|NY~?3JS2op8UUR;-6~%yBE~6 zAesQ{f7(nCEl4ZR9W*0}skovlcn6*A@82l+PY+(Ockp6QG_A1p1n<T&;v%Xp@VhCf z<+xo_fy742)Gsoc#3>||mLMS?r48t#JJY2x;Zc!@UQ>NkqWG90iG_y6{iIY}S+kVF zioUKQI73(h;ZvqaJJK_(DAK793m@*i_^<e&_9pAeycc@O?gKMwK?z%HNo8q*$<A&` z+Q0sYyo`cF{by7Ci13wu6Sv`RQ{Z2F2oRn*cwtHVAi_2Z2#V6sNjjD$5;g@qY?IDO zUvOUVG`l=piY~Dqk)bOff4J-}bUj($R`kA(xjrlNCQYcNqLWMHkU86%nNT&jzr8B- zy1OnY8Aye?t2Voxg?T@E{6y*Q`b@T@%pnYRnd4~T7f5$CGgc}cff#*>&P8uaMvKZ? z3L}K>FN^zB1Hu%A?y9zjvx00Lw?=Xb4LSk~9NjPW=e|9pIL{Y6hL9y|>2DFh?thiq zcNE<%jzw2Bh0pPcJ5$d>t##UT#rvQ{c9RIL@5bSlF~+|W3%35`6%lcoP1b$(5)~=i zWR`xA0S~*_gC*UZ&Z^g>k>WV6NfRtQyWqM(>NsT-SCGQ*oapdGQRsZ~J<ajkQ4};c z2}|B<d4%V}2vVuSpKu*S&VJFQ#n(GvomHDp6i!#0%i3*XHQ&CE;HBl*la@4`zp7h_ z2yHUL77=pTjH*znw_B5TS4M4g+Iyo`^p-O%U%=p~^nt_<Lfy#&t7jfR#BvDKGQ<1f z-eoHMYLm=O^5T?=DT#ce-RAPp!20&`NN;Dnq<MR+D5*(8LP9Tz%d!a-hxW44`)o{o z9tmIPo#3G0O))D5zP{I0j(qFG{f%t4^?cJ*ZtLxFAMIT)V;P5SS<~_7i?a!R_X$1s zl8JPYM=Qv;!{%^J_4XUz$~moOd9+!fL3&hoN!BedYe0p4H~~8+cmCu>)>_RqU|-P~ zf@Ykbb6(i1Uuz?7aoC&Y9K~n28P~RU-J7odPNE#SQ`S1~FK@~Y4Pu#ceg3x#@rWXh zVl^mc&OFC;2rdss?HtA+=C<PSW7sl~LKz(|(Uz~cySdP=`0+@XEsWshk9l-MyeF(J zmznbV<?noDO0{cCLw4qy8-tKT*vK)t%|_|d>Ul)G*Aq;r_S*0|HOszSOm%eq_=IkG zmw79ETV?v^b!^uoIaNYeVG%y0W<)+oSl1V>$LZl&)zcABc#w?KzwFb$|NeQV*}t(D zYW8&QcjSpywON6D5*Gt=s@d807#CsacKIDTJo?6C45DlWp*x2h_v!8P8Ef_ONE=Y$ zMR}=iy~z((gWhi&oUI04#_m21t+igzlTYCr%U&X9>W`6dulrW1Rrhtjx-ct(@Achj zp3oWd;?876*7XYcL-6iv^zVth_PM+hRK;8tlkX1jIagag4pTV}@NUGvx<sD2Eg#X3 z6sKqI+%9e0uQc7?zVt_i5D6{@*z_^i=HoGI(KG)<Q^CFV6_ros8P4XkoL1dJ#<q{} zUjLnxHz4%jVUHn{=CX{s($V{sag5Y?HsjRa?>BLEZ*x<t>^OYt-#SUf{y16gdp~DI zrq$Sl<*+M(ugq(+=u`M6=d;_%Z|*UgI32?mr9l&8pjr2m=zfwMlHVU9gE4>Cx9WN^ z=R9WZeWRTd)T_2JkRm@DM#w(eg|BZh;CRs7Ion89sjjYL`$?u-fa|>bz2HM2v6T{t z9YS(VjGJyeI?(&e6<WRft?`m1xBdE!{+vsnP&Sc?NSAHy)~oRAMSqCg{nU4pqJ_JC z=XITX*OhSY(VRFvGnTVcUrm8v^igsM4hlrz(C2z);of*)&Rfa*(C6cOZePTZZq9M$ z+vB*_*yq)rH%==!WM44pH}NnOVG%C9A4RyInCn(ZO;weB)Og%4BGuG2fUVnZD?`j7 zCofcw#ckL7Zbx5U5!)+Ey=Ws&Ru%q-3%J2Vm!>Cc(NbhD5ol%>teVf>Hqw!aA{&Uc z$?BbY)NN0F&m)4mRfX8QS9~}A{RUspB_qTci4^^~P_4*#c`fZ%3|-RLb=}YU@~1cs zP;M(J#KR#<s96P#6bfGesfZ%~CaZAt^=4M&sL;ce;wQZ`=*4W7g5cGMUsjQuma-7< zgZbul_{Y=$$+;E&%G#J5I~f$RKIseDF^^5Rp01LpJduWlr{_^VZTR|&y3Zd>UI%Q^ zVjoKmQTC3QcMZLI>;68NfC~Lcx5dNtIUix^g#EhYVH6hB`E=my$IG)RleC0NvA`!U zn-y0(WcA2APn8!ig#r&LV>jovyKJ2}gb|^=kSCT|6X8A?Ud_P+$&FMDVjI@2cQu|$ zKJk6hehiDZE8md(PX_o0W$qTXzA<Y*AwDfV;^GTa=!p^-VOvhe-%ZGHjU6#Z99@gk zzZZJ+0G+9^O!^fni{^$Z5C)rZe3May`&%!UWoM>Bu}{N7mw$#4?ks(`T`@jkxX|?U z^v@1mUW{Sl`jcs@JNm;6&-N3u)%S$ckW?wIt9N2QdZMZ2lda$tFO=k~Qq{R^-Rh>@ z_o^o>8oV^vwkpkT3yVsz+1Keqm#1qicS7UPp>FRi>lt@C@VcN^t1(<<c<){+FXvxu z&Uk1nvBERnpV9l|1zoYTMEvoy`dp8G_~Y$LsAc&NDf9;MqkpPf={3UCwv8SNAEH_( zqW19f;B@EBA(2ecNM`LCj<vZUUl8s!lQ20fq+;CPo$+-llu|Y^zAY}FxaO>Cxd~3+ znXh8iWBG#8pciI*aB|@Npe+~6v(3FV<?;S!*vl<9OW&X%w4)iQVdKni?gfjQbD1U- zH(T2Z=B$lN*;!et)r`-6EaEP(Q0qH_Lr*Uv6{#uSgNw7JoV7uMFK#mz1ixxMk{wbJ z+B`KW7U8}~DFWFdw*L|_UpkXD5&Fxc7U#x@a?_2dZ5%fXQF-YTzFzrNu3Um)<<{6C z*VAS6x04%?j5r_Y?hkCpOBA>Gg`XLz2Jmv($uo})TnVNA>2pwDKP-plxn6FEFwYI_ z@&j%0$5%fP^d_lXt(uM`_3@zORwES401Uj^u(H;-_GP!ll-jy7RCH&H%}Hm~u3;L{ zzi4skiE{fTYrG1Nd^8zJhWFtb-ZdbHUu(G0`JmizzMI_J;y_9SBGD-gn=pGqgy+%u z*8R@DF+)05sdU`*t0CX?Jgxp@2K3<GEo!e&xEX3kxKxB8Hh*ar=L=l$&tO6#g&Gh} z2PzV5LmoH%R=9P{!_s-{R-{=TpXZD^X-Cfr5n1qlXeKQYZqoD6$Sj^eu<m;^B)e#U z_!n!)QCK6AinWOaJ*_4`{Y*Jf2`3+b-mJK#jK&$9_9T>=+(L%zRBLX@s6T6kvK^z& zsSAgTZGl;&)#2yo_)E%j8c2i@!)AGqgr%L(We_s@D6VyV$6?*hcYdZwN^gdsVU*@( z$hsT|Zas;QetrM&3p*yl`|$AjQ9h)T{Gx;S{BT=crX$+Vs^TaB2BTifgBzN|{hV(f z95cT_iiUwe@~kkO6eYb_6++}|`3u6oS<)baH%bW!Eb0)|MCaT04I(;X+|Iukx|toG znRp#*>%Le1r^(em$z*r3qUE?8-;p)$CDL3_I6f?rD7`?y)fz&6N<zRug8A&U;ic5= zMeD;{huOXyd~**yd?#N0LCX!xNC1CvM3-$lo5dvCPSJcEnH<*yRU*N+r|8$e&Bq_( zeV4JR4K*Nu5}Yo0*LtsZ;c69YNJa87Et2jnM`}M7jj*)Y&lk&6NO>JUWL<R<4%ha0 zekFph#~=Ov5dG0#lqx)h+4FV?r&WX2POS0XNYTBmNb|^#5XxkA-)RPV)#$Jkd5Tjl z`*j|<Mt|cLqgvqyJr@{S?fM3fd>1#}-?$zLJ=ZupNqf*)ihsWO$?oUl`Ovhj!7<v3 zz9!hS3L|(SbUoZ9y*S1jrYB{g7rCHb#{CyU&Q60vg5Ft*Ltvm029l}a(cC1(`~MaL zCAa8=MHIb>hsFMdnR}#t{8F*7*#AM?QNq`#N<)7ZBu}IMmi9q>g`J*mBHsDrpX)x4 z@<MoadRmJG{Rb{l_=X|UZxUj?{O8f(Q2+Px|6BS0kFNJ5bFELk03xJQTw^hn`;>%- zAco!b%~+wD>hDC((fW<llI<~m&U*N7un85m1;HWB`7WiFY8-_p3b^JHt66Q3@>ON# z#}Vri(ZVK<9~8nOksmkW^b!ll%NCwRYt;<rCBMmLX>HP-BFf?Xdlp|mD%e5$(bQ=w zH|Kki(#$iP?;{Fw-Od^+Ey72L{u!tuSn26f92bX6idS#0&l-O^>rRIr*XV0KXr%t_ z{pp6V1FF<r(XFs=0D?>-67;A$a@e1(-|lB03_H9Q8pffOgRw<+d>Ef;xLgh$)3$ET zX>i&j(q?&40iv(j>E<x))$aGfx5Qi)<(ekH6LSXmc9`5r9qj-}ECkT2ofga~aw#@H zP}i?PeW-9Rczoye-pYv^g%PEbuj`#_ct&|<V}(B>-OjcP7|WX8IBt*1s1>}SdEu_t zbud$__&b5U+e7HM(6IOk>N9#@3nX1v!XNQ3LC%uPYSu!Kurg2ZDq89@^g?C)X=9Bo z8UFRf!NJx+-W8Kx6Aw#~%%5fRlh*TTb6TtI(E=_73nO<BVzZh}l$E0g2cjs&EVM>{ z%{RNl2;EJu<y^y<vG;NaoyGL&)*nj`Q$-!Csqt#Sx#k-jcV4^ijK)nsT=YP#G{fNi zygDBMA5o;f7LIk;{afT+JxV$P1KHKq3-ctQpUouKOHfc{+=l8$=|b*itQzGph`IV_ zCDGk}k{y937_FYrGe0&LQIFGgO1Qo~8C0{iwMA8*DAvrPl~4Sz49HnAOVi;GY}cVj z2Y`f!l&KYco37gO4n?Y_&tL0{i=$tWY&!Z$ygeez>X;Ra=b)A)iGg#m8lzY!=y@G) z&%9X(m{dzsy<Xky<EJF~3jBK<X$&4Y%5i|zi5kQTBS@kc4<r}7P%mXtc6AnbxIGzb zay<bj`lL+$7<9+aiWKIg<3e5)lrwbx8q^H0<wpDmy7r!*&!rldLQm<*g>DbUX86fC zJ}Rkl5tHv`>^vaMXG(-xxJ}9cqPh!>B2S~96C3Qb1oA<Pa=_KeMvxkf_3{wA6n}Y8 zDz|*^=O`aSnnC02WU+mtTcd(aKNdeY<zQl`g=Y6!T>B?SxoShhP1KTM<@ZYhDwEab z>bXqOTZ><0+fF4|3<gu7Erugr&m73{^rD16(MDNzC;I9A{H-&Lq{|w6a_B28mY?;b zkG0j}_}x=o*0Aq>2{mMVx86rx_$A(M(Rv@h(kdi>+-9c6<=O6Xn7=$)X$HOeEB(n~ z2gKbCW#Lc9!#q$Fn)l}#-Q)9+xnuWc>%U*z-yFoz(_sZc$hm5?YppD$FqIS(WnJ8k zvOb)Uo}K#DZ}>w5H=6<a)&m5j|4ysw;}T4f$^PY!8*x}>)b~x#luXIv>7gG`K9DKF zSZ+%dx1pBnl%%Uj`$_2>T(4Jcg~VnyN$Q6*JY;`8?*sqH;+_NTT1{Qo=<d%BrpI0o znIm|1%Lc|^yQj)e&YP62vsW^@Ni(8e*^zKtB_DXzP1f0FO#sF->0V<#F~IHVYiBuA z!)dgrX!CNp%<HU3+TALt?J1bqguDJmld|W2#0FGcd?B9dLuWs@4uG@}epd%I&;;2^ zxl;R0%u`Ta(-vqc{I6#XLD=EawYHWmNk3R<UDzNN$FtwIYP&|jT96DUa*d%=<Z@MF zRd=Y+Yr~-0e1q3)&`j`KyV<Qe4}V!RNqPbue=alkf@N)}FO|zOu@>Mfd+TPO;cv~? z+eMyh;zK!1$4*-#UBUd4*r$F%Wg`BMa|I&mB%wbf2|NN_MZkzN12c8-x(rnv7Oi1c z%%n4tu2=SV=(oJ|EWgQ-$NE14=W>uE^_JWHkv%iF&L2m1)^PUYW^wO$QFIzKuV;t_ zT~9w<W^AI2XutaSBy{I)hbwaay>(xs-&5tM=Zh7W3Kn7xH1BRwJKwvILP)KANS|cL zk_oKJ*dUQY^xj4KP<u!$k~Flpc}r820iljMkv(KlGCnw5!BFV8Uxcd;*4Id(`$=_^ zV7d{jx~0cB4`NU2LnCdwbLEo~c}?_bPLUAxdVi-v`Qt50)k}3uPwF6urRl9#>q!|# zl#_<~i2Bwm-))uu5TfHTmAjsX1{7PW54}Xd!K2?aP$iH;z)b>3DD&<Mid8_!<mU5p z-M=zS)5W|y>$yG9B__$QkM}dYL0muA19C-;<9w6r_wb3EWcgbN!{(-@Smz<?&R4~R zS6%0NmNfV7exEF;>NK7OEuEJ~uG$98NesPwP1(GX?&<7!XOiY={6)g1LsD?|xRM7F zy8gV*kHxk<YqC4q7ho_U*z-@n{P*)O;GW9e6xjYL=kAi=RY~M^s9-=}&^aRHcPhpb zK^eGM@V2hJkMbj14W#t$hlZkb4Cben7h}gYB}5SP#!>jqXUpJ(ys+&Qi<^w)Ui)Rp zrJgVQY!vI&?$ZaF;Pm&Lo~mHliZ&2HRn>v@fg0I=_W{Mm^sI>&epNP0qAJ#!^p6*4 zvSo(4^7NYBG#4m+7QAjso=;^lQHYUNP_X==D-L?P6pmKbdf#wHNxvEYC=y-c*VES7 zAO<XlOF~@wP2~>+iU_!Iuv*^xPc8pp+2nWHE%ysBxH#LHnAw2{UbfjuXQjWdNC+kl zz=BY{%A|-8Q;o*swqSAfm)gqcKKnLD>0E3m)#lM6AdG0)Fj1LDdUA+OzbWb`g-y={ zdxCR?|9*l@jSOaJmvWZml)dvjPq^RHg^CxXEqzk0+GN9Ab+<xyrz*q;sqaF`^rrog zY}t+b%VnrG%fs>e0+13ag~?oTlKHL4)t+kabedSG#R`kyV+(u{HuyW$J7c$pvtbdZ zbha?kx7RP0Cy{LNI$Tm%2l%)3+*;dMyJ;bj-U}VJJ$g%`jBKG61Lupk<lz3}OGFmG zgZD;?^YnA|q2Y*j9|;@78B#sxnR4fY`F6wgX<W~X65A=1h9_g);Uod`vC#ld7tp{U zO5(qR%H3y*O&802lo%3!2st?PK4~vEE;Q9@lx{{S`AY!t`Nu|9^Y(B2+`-bBR+H`z zKp9Q?aJs*gH%cgVF<92%7s0VQS8t!^8iIZv(f7hJyPoyCuaJf`ol`|PhINFPkEgRX zapx2%Dwh7r`p}#*d=nL(x1PEra=-i~1jM*c)UeyB{Lk$?{}GVX(A;=DU$g!e+YmGZ zEiJgK*hv0uA)1|eu44fPZs#tc{m3pa>=qD{hW4yPgeb$TfcnhVsv`u%A*+}65o#B& zrYW!dqy#WGh4gnAlfu*e4TiokeLN*xL4sWq7TG@*{`;Dqw-p#yM_)aAZGWb@Z8Id1 z-xRGNcy)F;*#rv|C!f+E8CSitsnyS*BYIN1;MLfSzn)CfliYY2y%kJm#6`)#J?U_9 zC?<EYH)CS`Ng1W7jiz7d^vlufiFcSHLCj6Dl9wSE#R}dxd$s;uyPP!>20gmRjZy;T zi%__B3f9y1U@=u;>sikXj9tU&05ERTm$3npW6bz`E#&!uo`uY0WV+VcIzmE%lltUs zKmc8u$J@OH@5SIctr9J@u}0@wYCG2X>{G-;eUZV6BA}%d0Et~P#cA4f8{mznoE~$I z6AVzp%&K(8W@0$+YNf6DmXVGP7?@P8HXB1@#t$1k&{S`rv+5ly)zuy=QdcDPJRNSg z2q`;5_lfmVQl?9F$t(mtfBBdRrk;_(<eYZY>&_6Bu?EMa2x(bRF1x|O)LDGLD~UuC zK}1BT(s^SJ5J9vM*f_0LFTRwNPZeONs<y~tZQM1S>$@=psJU7dGd(>olW-Fe^%)`{ zjB@N^qxei3+5d&vu>+^f#|30KE!}F!1fej*o{tH*A8uyC=5y3lBv43?gPJk=Ef}v= zHBauSd~ZRA8f@h<3V$OG7rIbmIg<-8RPg}MO4u`3Np)~b$Kl|X7PMl|DE~r#dTCJ9 z&%)_#;y=W70hZk<k<cXn7ovYIEW+AjY?AX2vHjn|*#CD`*n2I9t7En84hVh`P!kIt zAwT`ODwhiSHLlusV+_ardXplzGX;4o9~zh}0k$?U_?_R?<uShX$f8wD-IDw|;~Mm| zgnoNXGA<C-$mwi<r_HOJxZJ|C9d3#2Z<ePFdCv;^SgqA-Bu&Hr;b`f9v4#6;&&Ftr z=MD9nKCbKd_k{&|&81A*SyhsMB|jx4&{MH~Lp?54T4KHKN{@8o&NG|#;>Lx((f#dv z`5LeT_SEM6k~+N}?(fF_C<>7-6~NM8YIMmqetdVQf7B*Qb=3f>8?)^u-RKi?KevDu zd)KoLH1mMuz?ckON;Ztx#T;aYvb<3%RJHv+d2+MgsE9!>IICx3e>`noRsirW>-%vz zug~z8T8&PYj=yN)@>_4$SPKCp{1SW7%{}iT28fiFTB#22!A>vQ7dX@piavg1?Jxcf z9mxI8Mpf>y{CQSDbWkVvJdMV3`E{|LWKDDwvm@6H2w0)!cjJOU4B~G+ZZnp(1t1^! zH8?WVWv8T8weK7AN@aXiw%6x&5I?o=?k`&7`kDm1K#r);dZEP($k%2db4f!seOx(G z{0s!>aagtF|7UX76#>G2V;T3hYijDwLf&|(Zc(m6irK*w$j(*2Ye#SdtT2&Boj!@% zy0o@0#r$M{p;aFc0u|5mDHF4$AD_6$Adl@g2Gqidxod2BY>}E}<5&t6Quta<%R>Pj z%V!_pp=SDpzi~ZPZ*Kxb0!Dp4mX@1+eh?jBaC+UktZ*+c?u_Kf%9oK&ToLTotvq#{ z&~;8IR3191_@qgPO%G@$lCJ4eFa{8$&km>RY`<T18kfR_L<+pwtxv0TnyL;zsk8ab zIpNZf@LLMq`NOzGD4r`oDz&askz|%93Tc9KKZTAuakvCsDlMj7mC3}=a+P_p9kx8& zyX$j7k>35mXVu%4W!#gf@mdxH8G#ePT?{;jvmWO(dlvRV$OI<G)zIAvJ#*~A91rrZ zHGFa+tSdDP5Rsrzm`HFp-0klZkS)6P`5L5=Ul{klWcGV%B6xrKUV9;el>hqnN#o>q zlSm-UXOw4$+0wg%Y<Ad{AK@vAbVyL<yVP>?*+GnF4snkM!5Hxa?IS|<(EJ~KG*}Z9 zL0qcWJdH*ot|^KwB1H&2wcO(X3Ag_E_lbz4)OfGlmc}O>H592`<{BJN*rt7&++60( zXKG4<$nMVfX6B05bp|io@pWu`^MY=m!AEVrkM{DBdbg2G*V_xkMd%CxOx6Pes6aDb z`*mI(Uz7Vm^TbS}b6L!ZP+lGK{uM}uxg9i}L;#M?uJV)`^AN`r<bVaWgOh~3y->Ab znG<?B7^-tp{12+5+LZmqR3UUt%I8on2vV=7hPp5LTn^K#ztW131GZMeD$Y+p=6!3M z#PZ{SOj!zgvCnw^1D$p96)cszVa1!ggJhQ4ha}lnqTlKjZKU)Rf)`I_`-qG!Di{B0 zBJ$G<J$!u++iYmF)ep`NdqCR}XkpOf_TSLs?z!LmcN4{%l`?!~P4X{jd}yK+5*lv- z?Xb*|7^0JlU&LuONp<~s9gNdVG<zMH$~peb<9aJ^{^p?7x>IBOvF}{9c^vZ&8sBlc z$&;ndk3M?x*6!ERj4bD6-fh>26`^+V2<=d11y+hG)4IGF6?~DnN;k^PUyxRP*EL-0 z_5OsG<3nUy?HY>;uM?n{r<VgEmU>#~-vfUER0=Jn;PDjj3)FgC*-`6lMu$=?US4ga zX_MPwIjGld^e4QxtUs!Qh|_KROSsAS?a+xspdT*&<jND50n8vs83ZAC?7UGstP)6% z24Ol!55%IDoOC!nrK$6aa4XTM5dG3`3-b0(m&BRO_H*FV_jAlvuRAw=UY%LDof57= zz<4eH=!!`3zaAu)D!|_uVrGHmW&;3t2oe@i{U7kWNu`t!5h-|`e{f%_^s~gy8GQ@C zN8?C@RasPNXvM>qf)=$j&DLqI%6-4SyWg?#Vt8h+_`t0wF6qM{Wo#uQ+{qfYjtDv1 zJ0J0zi1-~m@*x$;cplr#@KD1rWHvStO@Vl68>G7qh*0dug=oM1j&^j=rf^{JH)Hp1 zk!Sp)F)cA0p#ZS<yCU;G=0lnbi=&+3!dy`5mYy4YQUmZDeAQ!H>)YNx7&pvvyV&28 zg)|PKS3jFa3rfd#o#Jk;zZp6!_t>33Aivv`g5}d8?Bqln*CBB;x4R}n>{q9FoH<e? z9!96Pbuj-2AMjsz9@VuaokazvOK5IM7_SRlLOlLxADmuPOIyy?4L{$>@HqJi)!D4y z$G$nFxlgtqczGoiPLxyHe6HmP=tCi;SYSL)6F!`EB|7>c`aR_)S)J?349FxG9zNy? z$bT@K4YE2s1c=BN1wnJC{60DaV@i;)lSoF{@C%s(ixpkP!TC`2M;C$@PD1grl98&d z7F|yVAA8!lw<tGvzazZECjJvMx<JbA&9r3&xs%=(>pnaY1HHu4Pk&QB=o63;u8t|K zb0aNG?A<pB^dRWKx>0%F_NYCD8eiMMXgF{=)axec7ZucjX(SYFH{&nOjDkb?P}DIS zr1x_nP6r9P-}TCtWu0ns|An)9x);4j-1QQ?BqElS1r~ZB%yO~bt|JgwyCN8G9T!&( zx}K%E!P1O%YxTLr&{@)+g^mVcNXIkZ%Sc^R6mSx@rqwuVBwgu4pdw;Qg2_-)qMHV5 z76qqbSZ22odP+^RdC+{3ic7lt$*BXvym{Dl29G`jYzm7Fn+6!Z!Rr8~FU$5hETJ_t zHY_j)S(+Gn_s74aMRos0^AviSpB}VfDX5vFAQG9`<pL_!0ygfX|8oU$QxPHHG6;%G z59Y~mK6tDy7r7eMxa05T-E4hB5ZE}!W;xBV^8j+WbI%ue16$8#tGJzZHTZR5*CQBW z4mL$!@@F<h(O_V;g;D9GxzzX5!jgh)b`-vTEYD%N(4MaV2m9&#d-U8#KXb4Mwb#$} zt1bIg!%(0^gh<-5$ZSfL&ZCg<N<!y!me;EI1tbaGIcR@Q8AW~Q=v&ib<8FnwjyPe~ zqie`a{EcC2{iT0~y#N7{|Gob!F2ypvW(zwmzlhrY5YOVccYk$uz}x3?mXhhPbn-OD z^JI48s?*=IUJPZ3?MapOpR;X*m1t8D@Q9+kJSBT#3K;)d%U6u$?UvrLIjzUOU60_# z5iEdVr|D3lCNiTG$XPXG4oIY?E}t0B<Eh^XHS)VH?dwZj%X80tAfzH+?>Q3Aeh^J| ztq4EtYM#5>15}d#h0g(^B;{eDK9Eu2vEcxN=)9o#cf4yYM$#3h*$-Ba`W%eJ_=Q#^ zNJ+FOhsA}o2=PhZ%96xLW<-7@v7aBA+S86(0#WBjdY3Er21)sq?HRWgh)^CX_!*Ez zi+6WVje$db#s*F<zC!n(ZWEgSV_p!?J?nwCp-cn=hp_1#5>&aLPBD?AAV8`9!Uo@a zt2JYv_UrZRnaaVzKrfk||Moblx34^cTX*@&=ayB8lq0k8#aKVaO@g1@<&ClJSj{_2 z9YH|!ya?!_o+E{iI=&3Garv&#*297PcBUJt*?N8hUGgmf4Ee$#R^p!exmWbZC<+#x z9|)y;2lWZ50;t)#nWKHRkl%KY9yI-R{-WeGxsDIuJvHDL8&#GfRr7m28ESOuAIsq0 z%aE53Fsd#9ng!zZ?*i^0at{3no8Rcc@(3_O`NR`dp<E}|9Ez5%6bF(A1;8`(X`C_! zBNx=iD!K16lm6jb8$UtYWmSMUbE!v`tIhEXSahdJquq~a6Ho(C-PYgq107KqL5K;| z9^wNoV~cVO-C!6jm(s`5r~!DbV&6D@$(kI<AhPq}fu4K`4+hWs#s5F<Sg3w5y82in za~8n%J`83Ia5u5A6eaJEoUP9?K7M*HXTtSjgfno`)A6slea=`#;6ew+i#6XbSHP9N z{`!LEm<?#cK8PbA6r|SRWeEK{G5H9b<mr&c-(>Uu{{Fx9^M67AARDHFc__^%O4V*I z4%9)`PMOnuJgn!}=4a7&fJvHx6ezH-y)Jaxn}$(9+%Cs!8Q6#;1jC3_JQR=lO;=Z! z8L(5D0neI3ueZWXwRs-Q&gFQ2wGk)%J|L}4irh0B=Dbp?wbJz#4J=BsYLJ0~B-J*Z z&{c@2d#uEvfa2NKAE|H>-Vqb4e>qfJ>MU68&g@6BWnjE-o$F*{P#tif!SiqZe?2l4 zAV4|fct3mJ@jpq#iUgH!`R+@4U@j4me^E5}Rb6ijz;$|~YUv<;Ao(7`MEnk00Axc_ z8nz3I<fcHjkdB2|?cHd;a>ibR$zW<e72%5gnf+{??Ku9OaP*PjKdyu+pt1!qw!fRr z60tqviJ5Kjbhl@EQAi**3hGG3a=J?SyUAe9*v?RfKOZ0#qpV&>=<K5{Zf9nkM!nC@ zX6*;31U8Rg7QORCx(7-&^Km8^b+pew($#esmIxWrah`L8<zaJ8Ap1sGY?-gD=LOzx z1ACk@XaJRK-(pp2=D9{^Zff4^cCoFX_~JPjv{#EHgUa2HgkgU06f*|Fjh+BNE;d9c z%ZHl}m?kSh(!>HT<$EtP)OlQwSEacHFBcyrse6kxeL}~VTxT9dJne0NoTlTpxz;WZ zxgqw}s<F^CUY`<UWsl_$2YwYv#LBUyCK5Z)R)Zy%!al%(_6@6OzhLH7LtN_12auM5 zKH1v9KHr~9l6|RS40w5k5qnM$?l?M9N2aH(3{o>k$2@(~?@=j`r8Q-j`}zT=vcM!Z zJ?r<f$Lm+CsXNXafVx!(e(3OV1@Z6&nXez-sqZ6XE!*+Xxk|I<jv$CT1`!9_S}Blk zhhD*4t|vVp6<U1`*!w;P0bAS6aUZK#9|d;JnV&$J<IXslB>G_#{L2v)g!uqe9ML`u z!dIP*i+<=$$mAYFGvx;U6OGQ+JLGP;6FT<cu#A-oo)@fFnUCg2SA@GS>ZN&Io90`J zD*zAZ9~I1>kR#+DC_?@}a_}*<W_m(z+PM1-LYZ}(_om0RtIfVnRJ@m3L3Vjc#L@HT z5BYq#L5EzzKnj1-t9Ea;7b7Lw+}cv386e}9o2=Kh-}kiDR_M|Xa=c7L-n=*v$1MX@ z9>R4HAUQi&STLWht7wi(bvity5?FxRM9U1tcsa8{SRJo{aMA>Wq@y^FT@6vVjBjHP zeX+m*ypl<OheW!}AXaf%u>d?r<IKgi-zCl)7CbL>8NH~Ux32*1uC=UpNg0HKc+xO| z*<Yevd)@n)-jGL=jAQl{&je!u^!6|`3i*$1$wrZ<6XhGI`+3+I@`Z!@wSg0sGsr&? z%(!f}agx0`^jTRCg3B=3LdzMX^vsxGy6ml6vyC`+z)x&9W{cFxO2M=%pz94uE64A~ zg}kr|#yQn#;Xi17XeoIQc=&GpsQuA$*Z!WX0#Jmz5z*cbQKlbf1(B~-qQJmAmjN-< zuD@|b>MSFOtg_iE8_E0QFg33CX(lspVw&cfg!6jVZs3#PVgg2r=_!XU%=1oac`f+1 zyafN!m07E*d<)1@SrGUD>0Temt&hgGgmnut<n$YjaMXVDg`W1)4FbQof+tuG73c<1 z)}*3gWGsFtG@X+eu*{27bT0ltLNpRe;{0RdKZi=xV(a$n{Vy-@4WvAmAc7-FgE^_C zX>ah^5L9N_H56T4gzM==2G1H-&>AlN4zMMI5;;tlsaAP`7L|%auOZSMRdfp{y4p)6 z{H)7(60^QBU!m+vO>MHEX9IK4(%E7%zgu+aWdp(z8q8?lU*1eG?!M&-!D<hJ4|^Rv zX)yT~!ENUaL_iL4qyA;X()nYFa%DlQr*pV`!8~Mz&)>BWP|6LxtU3@Jt-FXhh9%Vn z#W?0D>^Jv2kVj-y6pwI{DN+e^Z8|0^-j@iult*LIY}R`@`}vh#)by1I7?@GJh=OLA zfHlMLpJ&5p=L&Op-E2H=4a($L__yhyP<0YrZ64>K3Zpur(4A}x8!Po*R*mwu^>%?e zMD?MLw{wjU1cQ?T0<P|6%5v<$r=(^FIkhJ;RR?u;Yp;`#uV_91NAgYlvm;Uo`Z==e z*?RlE=KY2p7JaY!HnGRKcuM$H3B;L*%^YXXglvUQd;Q^6s7G?G+&`hZnYQqkroVRx zgrzoYP@fg2H!{x7&D$LoQ3&-aOJOOaG_JNo7z6V=1UG433nm<Vz50OiMhDa2hbR|n zX*#m|dC$suHl<rTh1gwbx_{7RaF?OMnQ-!|t$WK98lNQN4?mO~#?4X`Ln7*4@~tla z7~+7W9lC$2?FmQ#tj`amqLVYp#V(FZOt*3@kg5-ndtDmTV?=@>ha~-X$k7Sa+1|vI z)h(kz5ca&X>4J{|2P7$*{OvHx%KuWIm+;)_6W?wleY2%{x%iMSyhC3gYE4pvf!pO4 zn%m^8yN>QGnQe_<1euoxW`@Jg%7A_M)lzOf<6#!8N}imsYml2x$oc6d9z-y1#&?*& z)F>SUB*x8;cFvA=HfJug>IhIuEC|+VoW9r8&!r%e8g|bQEh=7@6~AAw^c>dB#NdlK zujmlhqck*X3967r?Vo#m$8~VNpFQK2qp!e~b?K;;6U@kC`9oMvO0$_$U=p8Z1+`(> zOOzRsJ(;sbf?8aN57FttU^pufEbbn_g06>$+oH=UI5@_9*ovFI6x8HrUCrGO-`V?e zqUbBBUc{k2&Js`#!{QXr`f0O;o=J;+6h^Rore`R}@iYEoSiS(O5>{ru3pK(EY($An zzf2uF>U;`{3k-h#yZeX4g6Zw4{ken`QscZE^)$%LnBzpPmbW8>mouI&m0+o-6)X!W z^5#S=!=DGMUu=S<@7!0TrCzc8xw}}fUIufdY*$R4f;mtm3G-;+0i-Ehb^=DBX_p{U zr+74HnBUxHBh9<T&U~Sz+4eg#Ogub}tJBK`TAbSL&FNOwY$BKC_H%61vX3oRF|_h; zY(IUPv}AWVe792Tw5K)GH_6(3`X;8<$>XpcSsu2+!)zlg&B(~;1x$?BwY4kAQ|HwU zISD<)c?z@X9})4|Wf$~;WfWiCSr!HW2Kq4K4peM;!Ezrslz>R#Ts}F@j9(lXi6c{S z_kD4!+z;%r4@WG(?Uc0()B-&mMf7XnGYCrx&Q2o)PI!B;fPf$9RrJmtUowrInckLY ztr4o-Z<O&rfdxO*7>G#R@m3S?GnpJ$GJ^@y2`sf(odx3aKxYU}xVj*sCqQYr0$_;{ zyK9a%OqkvPf|0Xi7{StZ@Fl7n%UQ9k$l%(HZ+f0Lk_)o$A$E^y&?AP;k=^r6MM>2f zo*ZvHFa<kU)vw`EvrxHd4bkj^MLQO^xr%A2WOJGg4h2VXb;m+>FM(;I&X%0V=G|A9 z<#(Yi4ro=sg3{3+F>jt*_zb9|!scF7YSF^qU};I%jr|}rFJ<g|4fQFQzY{oe!m~+7 z!r6w61ox``=*)ZhrOHBg-VEiGSKm2NY?kK!L`Mlo6y8K?Zfct4T?u0kBM7GNd3Ff| zqU=u3<JDhg9G<|Sp2(oeOoFWtS-Hko`LOZg2Q}4q^9dFc08s~st(y#T$7$|iv|mtw zK@7xTIl||xDf<8mlDbZ~zSi6`J@i0ncvcM5c3Q<>V#e&9Oq!MA;k}Nos|-e?z-h%x zs|RdR$uHmkj8{;!MA99pS?L|=1Q27fbH&yKxXK3dm9um#*@4KSOLBIK<8V}L3Ej~e zGQZiIJYrkhH*;$85#B@z&>PC>l28hILTDJ!gU86)7+fv4^Eo}6n6SjHt75Lf`CuP- z+&XX_&v&O-lq);T8&UpL8ujTM9n);SN}|{~=Cf|ysIy(sS(~aflKtu?P1rS=S<KH= z+H<>17Ykh8P9JL8%1EYlbBF)PJNoI(xLtq!Goamq!=Ns2zR9Ox*+PoakQ+pY;sb{` zcl}tLY0Y~Sz~y`@l4BtgtYzSI`)%Jl@%pDL=#C_W5@ylEiZUw${09yoP8uU~J0-mS z#^6jk@AX+JL?f5Dc}u=`*XT5tyH(!4^|Fgq)k`mFy~cWBf7Yf0BXuo00inzYiNQat zw>n;i)M0A(cfhO4^H(5()%Plj^UU@1cfOB=u7C8cxsty7kZm=nw|U9e#H8(#Fg^Oc z!ERfC!ykrc_<3P#*_O7dO{izAO#qUoZ9YvvjbUJ-fJ4Zjh$={z3V8eg8J&1SF8Jo^ zY=2H0+Si+S6UYn}xnW@|vf`B4s{q8254zg35b??ve33s)hchLf4+3ag$U($qq4Azb z;FxY-2Q$*J0A$uiOLh4_OJ;HY>{Ev`y)J9H{tWbXnQ$V`Oi%}J0Q)KY9bp0u=Dt)m zH7!7!Nf+@q>oD2*MT)RI%n266(3b|0xzT@(js1`m#^_<Wz4bc%slM6G-lzdq)H4(x z;b<%{#Kh<}CB`G@SwJ&@T|KD6Lt^*v>DS<az}8LyCjyb=&&2!ZD<vKHdWGz>?GKKH zLqYV1Lxd9S1#7{If~b-VzOItP6+&P|DKMWPV4l9KqBev7=c^s~8s!a_FZj=u*ieLr z{D&j*k^c9>|6A3t9`b*2=W0%byql{0dV17t|FI)bzRKC!m6P)Oh1pPgY$CUij{;u- zI{*lLH;9;z5QX7j8xsPi1A3t4-Et_a>F%UVjmrG4=XElY@*uJ9^Cz&UT>>NP!gK>) zk3b5znFqRStx_Po*<mbA<?3^^pGW^Wi>L&g-~ON3jzFjwh^I$KCE6Q61R3F+(0NlE zLdA&*2agHotAyo~e#3DQMXA`}0Tc$b3HMS~$FKJwQ)voFewE1}8Q<xUn5%A!YpF!h z#1Wv$vS3Xq4M;l2JQcdN;al{1WTvavZL6xawzf73QeiKcnM)kCf>B_%`H2>Go4yU$ zI%%s6ai>6{kO#Cl53E}I3_{Rbno!`38wSgq++iwg(dC#_5XjJu02ELFG#w7F+shXc z&)UB>%zIwURe_vboHxj|$!y=`3EhS8Ic~2WpSVpmI5J|83B&`iA(`fJ91^MzrbL&R zWtr2)g}Vq~Cpy5@txo~z@#mHl#vm=>0EAuo#)UMHR8?PNxJSol=`X-pjzgXmfmOWU z{t3n^Rh^(rL$Y~HOPB&kD7b;R8qdhw8YjzAA8-#=J1TP!ouR*BolW92&kayRR1+WP z0zvr=Y0RL|!>rSJ)x+I^cb?GwQDmyuGLPrAL&Iif=qBL6=NMT~$p{h|fB=WeN;h71 zlZZ74fEg<t%n7kD7KZk|RoY|)Ebx4z08)clU{&Yjd^s#s3a4O#HhNbazIgq7Z7bRG z8nk9EKpt^0Z@>s_K`s&L=e8cz8+Cn832@7N(35_4+J4~dmW>oR4t6bz#1j%xe2zT| z60oq9oN5(D9Kdbv%m+ZqW`0&gCJ@br%*&c3ap)8{#@v82J|Cu9>1t==aM&yg-@Wc8 zbtciZX-66brh#eihdWb%A~;M(o+q*y_@Vx3_4alHa?bC@nn^zvt5?K!gV2kQ2QL&+ z+;E@UfIimq$X5<2C7GxnM={K1bz!=CXRMXTZ^HcmnT%k6@?pE7&1u@O?AI+=KkHO? zHztI~^>+c1GQcXPKVByTyTw&t354toIRjnfkZxB3w{>bkH=#-L(xDf8T&ou$r&%m& zx60b|lmN`ddP|cARtStOL6Lth)YzEx;C{;+wX`<cDDYG#kU0Ua=zMGi{=;Hl)In;8 zy)YPjKpAE5m2>2wsy}|H^Z0$ZMEzACxG=`P7Z=xYn;7x?og=NRM9W_T7pzSQgjKUL zAVT0+IxRM4VC82x^&DP8*vQHe&#&M*klP!}-C1M!jtr*Q<Ky0*-_`*vSu3)v&8j&s zU5`H)DqqM0d2L|D{n5`C55QjXhcbx7@W=ViMC6B6mUXa{9(m5=<oD0PKB}Fayfm+d z;}?vKDm&-EPM3z7C@6{_xt%-zQj~l19XzO1YTqzN4a8=?7}LS@29E6*{Tk{`i@sF( z>vNRIJZJXa&s|dn(aO{vz<EB)J^&o#DLbW&153X@z98{7QGg*pZL9h&%_f%$6B66l zD%Y4qd$8J=$IapQkB!4GKJ0F%9~WG_$oRrfvG^2YlI)s+$KPxCYhzfkB2YtvxO;ek z&xF&!XqD;gi}#zKr2I|<t=mkqsBA|r?5T|TWEtp1U`^^mz)FN4W3pGp420T29_jzI z^W9HPc2SsspeTZL<U^!M7f`C9iqfPP5u}44B26&#j)mStL|Om^0@4Je6A<Y{iiTc- zgcf=UC85aP`1$SZ&g>tsv$HerFPXWy@4e@qbMASbbIz*=98LpYViC=wUPOG4qY#!G zMua$Yae&5hw4hh)UzDpp-e}wu3Paf~?FABhtLJ&MLaX{^k#D<%VUa7Hacsryd(XER zpysi!NZp^}w7!j=%tmlXI|Qv&bOpBM2W*u@Ms)=+S)glja4w<CLzGnIMLv`bR2){o zDekBM6F(zu)VddC_3?SZa!lycAB<c@R6HM5vI^uKR76)dnr?n4HnLfe@w(N_#@X|g z#YPg|r}1F;oe!ISe8&^|E{r)jHD>4FJftFIT0zKP^6hpnl5OmYVt7IFRuGX*Yd@ej zZ;51LV&bzpY-kd}(w~C16A4>~2QLS@8{fRjX?*sJg8Vk89yupg(ME#FA{#3u>zJ;X z?IUf+g=+M&C3Z<B7mPC2BZ}e^+|sT)OqlzS+xsg7JxbGd#L++*)E<+H-wXx?&<^if zt>^~{t8ncA9K_wRBbo{Ju;;NX`-7joolj=p;cGOUeR|B<SUXk&f5OpR*mQar@XCa1 zz$@ow6#Y6>DgOaM?Gil}TP`ivzJxfnD9_`TkftPE)rG1xJ_Xj3!?fbEhTfN`)&8dX z{<?eWZAjom+g{eszGiC*zwc43JIMPvcF2Lhk+P6kr+f+YvRpw=EooYh^m&=GdO1Gw z-^JQuo(9v;Xbt99<*afMg7C8ue8Q9s%A{1lvt2!JFN7Uiu*xa6UT5F%{1Ij0i*evi zicAGfz8{^T$J<>fP>nRq>b9N8Hd5BKqsh=&(*!<Y(9ZoSbKYB`zE8&umW+Hj&s+^! z8pc~K_kn@GpC@BNORBpg_TC`}Ry*8N8%@SQWAZz6z@~!N)zkIb89sE=(gi-pp#^zA z04P%$GbK_b7VYXD8Lm^@5K7!zddZ;6Im@T!HlK`JvD+2(*{~S|WhmuS^8-}_idvp& z375zBeuEZi1K<IMMZD`w)ofcifNIOrUxv*(Nl^NSkz2Y&pfK0EYUc6pQtLt6Qi{!Z zFvtSPnB+fIW}#!u?dyz;MD7)s`8w0Dpa|WmS5DsWST~)^=`4XZSjlt@Gx~wZennay z5?{~<SOPuyj9jaby<+RM_$bPwimt20FN}}obtOS`UU;L4OV4uRBM<(31Aek=>GxKI zQsL@T4;P)}HU^+(*+eyiRzdhTl2xkv$Mec5ROsiY=U8Vbet$i4X%HaEyxv@j6OeAW zDO0q~SaBGZ{FJH}2CV$r_Wfo$Xnn4(+Y_7wD9+&{_LE4(&wB-mDbG|oQ0b(Jzp_^m zm7pS4&C+ZTzQ_rAN6clP9>XC%{YMl-!{xB^d63?VFBMk@j5f>U7QF)U5(atCL(YWk z{!q`5!}zUOPNO5}{Qt3O3I}c&0`3X+t`Xnv2dR80DQJ(8bb$x5I3zI6xwGR}(x<4d z@G?w~5nKw29gIc23H5)TUczPKxO-D>{1(4je0+~TVl*9b1&LZnqIQ|s3R>;VDz_Q# zA6DG+uSW=HIZA5FR-Uokr?s~$1MC-rp4&3$M);>;-F?P_-)|y=)`Mo6k3tS8r6qkg zl$t%<Ds>1_>O!6sV*5QZCeqp~wRx=L?evV<_39j4Le^!Odijol?hT*u(i70Azhm6& zY<#2f(QNIb%+X1mLw|;Bmw41dBDGsh1(JvRDckhT#J<N37i(HvQ`rw>FD?6ysmU@= z=*hB3u{NT3{NULq<@W}8gCH^GxG&kXyXn|A4)WVMHN?Z4ucWiczV^whU;2cY37~u# zdGVAp%Kgx@C?<2b=9LQQKYx?8H8HSS$&9|`c+8s_S@s?@NKe(_E@cVS?kd$_S9v_- z_uMfEponDx`yFCFQyvk>0kpALZ8ssRVa=*#G2L1Fsyt#{Q{B68^|04SRyJ4;{i=9t zcy>Xp37Q<01K;!?T~;1MIbqnrvdRGZ3etEiT&LKBmvzi&j&)g=80MxGrw_vBM!c;< z4pqh$sFJ%X|D1KXc?_fzYje+(4sF@!T~syVIbxw#s%2sILdGjmJo58Xk2g)w2G2+7 zB3}(3r0KFe%Fa)%`w2-3v?q5@9d52P#@tiClkn3E_A@DQXGhn^*!`YXL3MSDc4I)_ z)$Sx|b~TuxV!hb|(Tv$_7q9sh)1yMz%cLYk>XsvD`w0$Mv0S27W)Z(Z^n1jhIXAr& zgi&*=78$#$`lwg;s9ud4wR^Tl(i!(Z*2=Y)Tx06;PL^^HI%{d7(mu{?E7w#LQ@B$g z&aSH9z;1CApn!r;dd(9_qE6wbUjBxnbdE|qwa8^JZ}zUup2o#tf=~GuaVBU~hT<NL z@&We+8RDJy=gp>Ut$X)+QIOPK)$0V4ZVMhG5pC|L-0ii~-sW)!MMwdYnB;h|zcdAY zt=e(iLDO2T*ZYcVs+SKs*NPYRon(xq1iQ7vxLx8_l@1m9y<gh`sT}W%hp!u2E$8?y zWLVLr86+<ImHWqxAzcIacU1fX{YFiudP{w!SjVUM7f=q<pKb5z82tK!va7iP=@7AT z<!~_f<C6gx;`ns-I0MwS4t@<gaZX7(h87)X3hC+n$2#0c@k%VS_Pi4Igqg!Y5%PT} zY}Mwn?Ya-4#~6a{;J#OCR{a6wT0w|h{o;OwmLD&TyQGm{53f{ye!lTH0%XhmL$XB; z4+{cBk17j2cQU=Qc!Qezjq^JsI94Jjl+Urxr;7Xu#dFXMm*Zm`nkKOwEM41a7%w!; zYi;6_F)N4^isOw`J;8Z`$CjH2FxEjU;hWC*tob?f+I~>xC0*?r;QPPhAS<2cF@*!~ zZ)SkPC6$1KcP0$t0b*&7-O<O5L)FfXFlo1}AhO7Rxdsw4W>U%qiZc)XAS6;+fd_zl zuycjHW&2;@+1ZkRm%yoC0*tg{!c#!z4;Mi~X$pQbUo`!LD1fn;0#YQVdO_dq?{uGm z@rYdh3_Ko@__$p`ADYABjLe<-JDm*xWac+)C;&Jk;xjv55@oTGcX#XmPWKh;xYp+D z1>)|()1gTcZdD=@;rTD85*i9HpId7v)8Cawr;v~%OwDN)&i*?cX$)Y?!byeyu9Q)j zl+wSV=1MR1-|7A@-?(0DA%5RT$oeKsXr!;8y(W&C+q6w1CvQ27QDjv+-!;JSOUJgY zN~kQ51|userL4@@Mw*Wyp;AzF1S+u6%&qU=o={{kI=OG`-F=QIqo=BzyW!2xSKJHj z`ohgx@8)gMM%El(m0JF-;VTrI4i1TtJxrgfVY6HQ+d7uf()`0$FV(N`lbeR+!e2GO z6r3F7cZ9K(kDH$Z_>jg&KmRtBVVi+(o?KH?z|c&yU2jqnf>SIhn!Iguj+C}ghB(?- zZHap^V1Fp%wX*WcxiF2k*P;ep0*K%*RGL5o^;2#?p$O`F*<3cgruqG*c=_S0m(!|h zE8HuBJdHC4tJ{^q9pGXoAzz^m8@zB>vouiXLT)AD<-PGKFoGIz2T6Q!mH|TPR=e>F zRDVu-=n_!i`*OD$Ik9_l$t5BAk&=&<YcKxQgxd%%5@kIp3UUNGFnO<xlXlmC^qK%I zWoYO*pdA-3kWbI8h9(2G>xuv>r+*P>chQ6J9G12vv-je62C3TE>w%c;)k(r(7z*Wq zjkfbfPr#=^%-Fmr!Z1L4^{Uy)5xz>8z|<?@F!yg7#CuH?*nkmZ6&=*^^rXk>=l6^d zRSMPpop{2I0^>OQTFlc-HR>Lx5epB2hqV(K>I$AZZ>tUSqg<vFjVEspt{U)s5CF>G z43;7&IZo-{By*~vj@vxCBXzZr;AqMsuUg?Z7lrYdyTYi|Bxjv9SyuZ=x2GLhXur4P zZ(be!&b1qIuxw9w*P>S8!_i$=Lc1Glz{LrykrWuK(N-1xu3`c`yRUX<-{RWz9bS}s zJ~kS{+9mipbbI3z8IIOss<y{0T*o3;Tq_t#CyX$eNj*D3+p--6=v0q~Tv*nRcWwjY zB?Gn%|26txFiT28pi)XoJZ>xH`OW5+d96Nv<OAU|0uXkQhqnS`jF#WWM3O;yfS^K8 z7r32?rmw*RL`Pawnv<&C5Uts4mHAqVVCL~%uLm;A_u<Kk=+^LQ?pV9Id>ive9&*HG zb7}`*<CH?+$UZsXxUg@asu4!r%igtwU~ZIqE*5k8-e;0KW1aroRkfiy3e}p@f;V2m z3ZrNTiS)}YIzF9BR!I+!O=B}nhf=IQtm%8wJW)?#UoEt!9=PqhM%sOU&_TisBUdVD zXrP-!jV!W9@-<F0dsi>V``yyH#Kmu*%q!QD|E5}49%;vqk#tpydNHxB-Shi?W$?`< zf{1PJG#@!JxS}NrxHMf%Idy(ciA@{&Y}8&C*{Q#V;3~36zQildKdlY@`a{%buX2MS zy7_>G8DF|m{o#>}r*6G4t6K*fV}@sfu&!$ADIXlS*W35-lr<$@Yr>1u<d~qR?=pg8 zY_X?5puzjJu;fX{dDVUPe4wmuaWl*gwc!)xR2|-*_0l)h`V9{8J5%zstj-l9C%ChG zY5+x>*A!QDC7iHAx~%$IJiS9bP|W;v@b0BHLFwmTyv6mFN!lA{l#}iTKbdOhMzSaI zmeaF8_xs82j1^V%1%RCLEK&08I6>^b!1P0<2-W@C#nqx)hwAgkTjh(>C~h+RMjYdb zCl{p8Twir_XuxM<T%%&C-$ym-z~+aDoN|2>K1DGg-u&f2Q%d39!(JuvJy{{bV=iNh z;~HV=7M5`_{lmr0SRb$OtEYsP$n<T2`hVsEaU4QHNCdgT@8n5APdw{zyQZH=;A=Y$ z`PmCBo2_!A$cDNpd23EA%=8yv<n*VA#=DP%=O7uMsm^%xWcJn?t)*S<-3Y9O81MQ^ z*C**()P(3HjR#(HTNU!8S-ai8{X&47ZLNLH;%)_t@BDYZVA8JfZ202%+t|iLc*>}N z+;(BzaVmqK$`!3<+)Vd;*c2Ups;NsX?e_f!rE-e3f-%%mV(e2C#q>iQUCMR8S8Y;! zOOd!pvn%cxb?eRIg3leTdV_OH4M~LxU8u*~PlO&4Xdo74?FLG4jn(WY9MRvKCS!*z zvzX3T%u_^PXV;@sL~RB}z|jef4W7&|<RBFWOX*O|bQ#ymGmjmGiquHRyOIp8sLh&3 z@uL!5kNK`E4+=m0dKTmqE))Hr(W22HZw%gHGd+QC|C`>hn~ym@vro%!{<kxYsh^eQ z$j}@jyC~BiCim8k1II|XRL(7X_{QEpn<jV6@zGnvhxLm2W4vv0LS;7ue@R`y88gTu z=&_#+gedh&^YkS3Q+TtTyx!c5dD?63tOHgn$i(lhaz^tT4+#P18{XDq$TlSNwCCF< z1siNW1sf0GFNyN3NOCUov1cwF75aDSA>LsN?cuNF-|^+O(YIiwHl9ctdwdi-Fw~-3 z&U({5Z(TedYk#(V_28U7rirEg0L8gXt~~l=iyUYnEl~?^9^~h&8k8ygjMpZVE<yJ2 z9tu&q8oJL{1>McxM~#j>V({fx$iNHAhp*$2_u~u`;;00q->H|oVEC=?wL4GPai0<x zR9O(#s)JsRcPTVaJmP|t>J#jqTbU?Yg}aG~4R#JdHp9FPJFm99cO?0TJrBK?FUjPJ zFPv$$>>e*?QdMHtP3mLQ_H~UqVD0rGff|;lKf-*bd3#G(>ZI9?8TX4dTyk#Z`t3m! z_7{j%I4S>JD&OHb#dMeuT(`J`-N*j>jlFyKYm)`wKjA{~T(4Vx4Qfq|Bim2*$rdbs zBS-!IA;>iyHF{=Ql$*=RJutE#U}kRKymvufs?Q16t%UYrG(e54-B<pxG+^8~vHqj9 z%15}o{=jf~G6d7Lx2Q`obAf*mb<S6f^C63vazeIjr22S|xY}b}XYIo#*1oo}Du$#> zr{6x}1<UjeMVE4<-#!_6jg@ot>qRK2nn4#AqE57LiD13qU19^#ZE|mL$z91FT-euK z#B5$G@k*_BP^7R_gY~lpHfFxxacz2ymJl4OEbDJXW+`M8?n0dVAgn)+<1X1$BwJ!# z?07=*VA~wHLWD>HdR(R8;s<O)K4Z&`YUe=Kgy4gXk}Gkz*A3mT#RPKYm_`cGyx5G? ztpyqRXg!H--KMCGbG3Yy&h${{6M6AD(dho{_wUShq=LEiN;!~5YcAZl2qWlOAL+4f zkNRlQCZAu4MneH!A+b4=erC=)-WDeC3*l}b!vMJB<RqTOYj74_r>lV<=G-+Fzc_Jq zSj|=M@WlyJ_#XI2$NjF~<Y#Je5js-Eb0pNLdoS^CI>~(yhQKd9@W$vkCQ(y;cO!Iv znW@Tg=ULK*!I-P?eji+|(~6F|zX$m8OtAGLnV}Q6vf<sy+{N-nA(iyIsCt<Cv!D*s z)%AN3y)D~iO_EzdR@}4_?(bz^^Vz{}MU3JXY7|mE>W-}$O0$%q_PqQ&Sh%I*)Vhwh zH+@-L6fKmEdJTx4^iMQnk0!VDh8+addp?@{X@ZwOcp)^35A`vHf9!F>!TWcQ^IW(* z<K~Cs_iKHBS@qf-@cwuWdE9=LJxE#=Z^&9T7qrTv(m0Y)we=&_U*G=7JH<MS>M$x0 zv0hQ+g@ar&T1!rqUv<zMRe!vtdq8kE@GC$4*sXk;a-Bu*RDDsgKXoGTTjQ`vh9ri? z!#@r=6wtGJQfS|17VU%KeZ?{L?qc%FmG_i_S|5h#rXFgz(X@*mKxKVFh)79BG+`zj zFLv9=R}#I=BFhpTKl{kMsL!fQ$7AzEOf|KyWJjk%533HHa(}bo#Dm?*Mq4^Oov0}2 zQy6e?H*1$5FG{)zH(Lr1X0%@MTv593mnDKM+BHW1fVt&Kc+Osa{`UG#e>I~eB)eBE z>pT0~_ZQgxs+3empQ(3<msM5<^t_Bo87t1f`p4?Np6}OMOCfzwk~dskb(LpVDDW3C z3Opt%iFApxvYw>UdwTwh+_Jk+TnhH1-2_+gguCCTI`^HFfgJTN%Z-L$<1C|10(58U zR)prvma)ghY#ycM1ljs|RKqjgU@~`Ixp<^aMt#E0k6M=47_Dn4*^9FJ(ncFAh0_Dp zTK!iv$f`ErV`~+%f_q<N`VCg(>x-{k`82;STxdUR=+pEi<`K91?`YMK{0nm{7xPL# z7VqKE*YW;B9N`<7XciEK*?{y3e4?TzT(=3A=^d1z`D(PD4QFt@IYyy=(KYDWq=~_< zPIJGc)Ub%6X8YmJ*_xsHrxS=Ucp3rEl-*ZwVwU*)Vzx|z)IGj$JOkL5){W#d)il|? zny!iFH%$=6UtIcHNMTbh?{@eUbV_F|@0gtn7hUeu!$|BHdb>Vox|igxsP7)-t@Kd? znOvQ)YR7scz~&Le69a>H7vu-F1fBb^uTXq!UpfV4IkBtJEXEuKTDpv8=rpuzv3&_v ztXe>N#5=gvnHIdEb^#h52KZY&Dd$oOh2#Od)eF&x9>-RNU|FeP#+VF?lTRT@eZmS> zJPGkFKSdUco^30(8Fr!SUf8hPqI%koaErA=24ap~GWQR;%RS8bIKoAHCRb-4c)JyQ z6<ErDima4_4$Gj|STbM}_^#%%q>$mk6w%AMCY7Poz&YF?o_oP}&B1jJ^LwX^_}JRm zFHZakQx78nec8&=c}@NMvhrSII=y0wj92w9YTKrJx@>e$4*i8vf^UpfNpQ}t%~|)? zW*aW!hujYDb8lB|aLSeh?DVC_>-klUnU8jub&ba=nXhp`f3S_U&oq~)mOD;vW3rC6 zuD3a)u#r{6Ej;8B5<cZ;DVR829Tg_DUEhy1ldmbu>*jwUSo!=6ufIIpP_u=_x+4|& zJRak4Q2`+uFaLs`fVa|wHVW6P4Vd(HqC@WIC-r1&JUS>lDqz`utah0gau+%hcXzf& zZ4fv9x^jO*QpD(+ab=Pm#fb4|!grZ+CmbE3JuHI1YOGT!EWLFA-upXDrRoI%KTQ)| zW}U)*ZX@p+&2=95Hfa+TOpa_sT-|l$ymC3UR67U|$40^Af|xzk!GpbXp~p`_YP39! z?hfDp7-69DVfZZV?zfQGy=rpW-NOlS0TzJ2bO5CKK&o7;L^b9LGQt^WiT-U5KzfB? zaWLvIin-YW{HvB!lW39>z;8s;ks}rpcuTlAv9_*$Lu};H0PrgeThZ=f4xb%3IB{W* z-bn9|fvt)VM_(aF)MV>#%_$GQnCUF!q9hL^j*h0?<qYg66rCYgP_jr80zvyUz%f*M z&EeDAb?>U(40x*Tcxh*2Jn(RAT&s@9S&Hk#bw&P#t#8L_en`Hma=c`~ZHu-gB~{JI zds$xQ`o!j~q(b68fUkZLApHU0D-{vG{>A4h>tF54R1iNYq-iyxAu$co`Rd2-|M6M~ zIIsmnZ|A`1C`)rpwl1ZuWgaVSEYcc?iFIjA>+oi}i;IhiEO0z7XM9fjWB&V*zyt>W zOkBAX&?PHU-~|m|G*b*3+=Q%*YDcxJC16}!zfiv~XGP?l80gRn7?R~OL!GL!j6&`* zIWsdl;c~~0qHlj$I4Hb#p#DEef=WJGe`OZCW}dwg5AHj(=WXn-wm(qKy3OhP#|x%N z?d>ql71>R@jBXtKl7fJIB<XnH)qfcYyFPga8_F}_e4c^eGhBxB!XMrS2&|qVG-s)w zR*=X75s9EyfQ+cn<O2WxB%z$w1;;OUH}Ajk6rg-Y%$FgD1pJryB+7v!+_;!T_8*x8 z3TP0&bVAAgn{WfHu3kdr!r$@#e*yn5NFb3#+4{>9E>JgK_bcTtDbYB<!|6f}I&4cn zSv(k-s#>9oh=c3(8XR1d^<E>nSx&=v{NJzy@)eaq)$elN^0EmXDnI{f1_@ajZQ&ke z5csYeEl!(~=i*<DT028l&Ac0=FKz!HG0K_96ZX&O;B#$`AAk8s67qdgQp!Wc(Aew$ zy<`UH9f2=)_wNWB4sic&p6Tu;`?FM#g(dp6e~F+!tN%Yt@S9_lERfmZSG*_qVm`?| MRb7>`J2v6}10uffhX4Qo literal 6705 zcmb`MRajKfyT%6?QeXy@Muw2?Msny<y1N9FR2qhs1_hB2>F$v31_6ly5y=5*knTR) z=ln0u#kn{a=VCuId%ru@to8fe@7>QtX{ad@;8Eg%Kp+BTB{?k+2n`e19^;?`N2>ka z!+{SpH!VdOP~{l)Hn4;1s$}Q}0ulBCTOV2^mt-CYMDa^mPFly?Y|qr(-b6n@@|%sA z&{tYxfn*aE>{|=h&=6jZJdQr=WUeqKcG<;OSd>B<Z6YzpTIZH>jp_wT5P#L*sV$f( zPXy|oh=1kyeEKRqLdm#wfUu;b8=|ekFzdibZ|FGkE&o1X^IptrW9&x;@{8N}mn*^% zTf{;0=Iz*p7)oL@HvfQXc5#t}1rEYd0-vK{(}KV<;4bVJShP?&kfenWC^+EIvf^F9 zGc!_scZT+FBJVoB{qifL&bu%6{fMnT7ap{9W@-85!=|($DSs-WVV&xzP7%%xv+`db z8F_TV-@R*KDA}fZS_#?i5$+}HL_|H5zPm>h$mrTAdl#Lonnmfw{w5}oc&ukyZYc^1 zZorAHd+0b@^g`|7Ec+d_4|>@1sZEcxuD2b7M*I2~cItDp{gUYYQ+3!jday5%S{|Ot zdf9;{#3|e{s@jYzra5}HeSD47b^4Gh@4inN#bnL$RlQq@vDG>Sf*g^&qg$ecFcK5F z<)0<m2W=;6ZfhA=t}8uZ=NOWH=Z@@>-flNWIiz_iUFq>q_L$AxW<<#!?V@d>w4?fv zo`?nAk7z%*!l3t3>w=wjLw%72M71^ryE9d-sj7W#is1dFc2&gTrjxAkaJbaU>cWCa zg`R`?U}tNK0K3&Dc1uSa?N&vnYU8C8a;T^E$IKN`DapN*>8H3pqm!?=^CvVoF<L27 z;bFUdc`DJo)5{vK<DvHaWjEdd*%>dK0vZhBtM=Zs(~M2kUq^r6+&Z?&5fL*l`>9Wg z=aazA@neRxD*5=d+0C#-(VTyJcY4-DjYi4-^${@{gTm&91_rRi*Mrz|H9vlEfA-O4 z*u1kkl;1Y0(0YZgksz$XC|zqf@i00zK7QjhR~S*O&1p)|F?wb=$Z^uL%K39zOE(~u zBbltHvbRi05~`#Ctn!9kZWjDS((%fW$2V`MdV?>=&ZR}pOz#h8p!edfr=DrkbmmLz zE^c?+S#5dm)33VgjXnseod_1b=%DuGtjYxi|H8=^RcFo;2eCt4AERBHC3G;Vq)+?K zU<<EE|Af=RL5MCB5IPb1GKMV%6a?eJ4uTV-8^CzDgFwHqf1uF@ouRpbaOxC6H;g}^ zv_W^o8z5*`04{LMqVxT85O|V4=tR~iU&DnQxH_C}gxuN)+!ax$l(WP`3zEc_P39e_ z!wG`CPLOH01Rh2n&S4$<ng|4&q!07$4FX;A;2RE*Ptk)U^X2i*!qLzUEy=&d@)pW> z6?Y8O+KoG-I_|cRd3$?%;UGmtMP?=@wJb^h<*wkmnmiil7A~6csi5;>i~OX|72@9S zWf<4wM6rxIMxQJk4nbGAP_5%j7kO({B%fofBRyU{f%or{JcRf4>9*~>42dr#CJj2e zFO+9EqN0LRRHlwqhfb&*99Y)H$9^oU`J&}FWq&JjIh&sQdVn?KMPpiACozGc`?9*} z-q3@pBEG$>d>0GMT}GX_(hRr%aNErucg3Oc`e~&uslIKimUG;ri!w<?(Lq|JFD4Ft z_KsCJA|nx=7+I!9^n14xqY2=C`I=M(=4Sc4tHu<i6qHNV(50&A6Zs9Z;YU*4Q!%5l zY?&u(=J$fQ*)eZk<8k@<W%OTX!jvR&G->&x;1=eSIq~xtw&jWAgpYOfNDG{s8~Iqn z6Q;G=mrZ}96t3BhUeF(xChuy~sc*1nD)*yqrv(F@Vtiuv=+wkp?7)9FLrZhiU37g5 z3RxYAG)rpiq7|{HRoVMbj?{(*t!C$=Yi8tFhN`%zR;HJ&%9*~lU#>*Lm!#yM6kAaP z^GHrb<sb#WE-}##!-8-DcVX+s&-e1XaF6`kls`Vv^X&o4Zk%Lk`7&&QBGe!smB5ks z{Mi5VzC2R!^?9z*vjR$Vwl9jn6b=jy9-ul=Z{po|fz_U)%_gfa{>dhY$}1l992yE! zpSmZ_+VsVc@-LLTH_f3+9mlJ(MopCsvK*$ef@6;@eSOoG%7vX5E<LoHeNLDNzOZdk ziChsxX|X1u>CdapDjHuZ%sIXzYy5fM`J3Rb^|E|renrsFcbkgZXa@l0%608~0>9l@ zx|rAg&C3D}mI+QHxB#Jc+FQnJB&%O$YK~vbn7_gyz4X4ZDtiZe$_|cDE3J5_H;VPE ze@mJ~JLP<eg12^bUfTdNufz<>bA5wo2XFQ82Qwg2B0R~$h2nYVj&BecxLjC*N;u?; z+yum~<Rfr^dwV7JrzE+kC&&q!wD0(6hjjc~$eJ*qWJsAX>0X6{F5@Xqw|@WY8IKY} z;`wAji0}Bm?v8Zm%b{V)(n?=|E@FcvA=&^%JM8Ysr_KKX)FEFe%~Rt@(4~c?7F5d? zxYrlyj~_VYK!|D{RvcD-pg@0QD-M#<Tbv#QXCFAN;FI^nJ6`$GZuvo0*4!Mv1BEkj z;py-W-?C2O3gmx4mu~4yFv|k-oxIRnYb9CPw<X$FLW)Uew`$6ZNk45fmzn1ff~pL) z2tE(zG0H=1;i6LS$zs~yKNj{>B+)Jipw#IlzE5}ad_Ap?v-wcY6*bAIH8Nvnkcr<< z0Z=H5uac~z^2^^QE?<eQ+~JlvW?jj0`XYXLo#mN1JHdfX^8vi~rbz15OV!DX-I<-K zGx!nQ7)-q?n;noMz(jlztt{u6<nt{u^#W7>b@+JsWz6848e&{GVFz#R=@J#Bxt*RH za*So-@V=kh0F+G1zD3}k^x+5F{CGs_Pa*Z6$vcPn6x4sn{n(Dysb{vnvf*~Vn#8E$ z`dpAE9QC+tL?JQUtGsl%p4~Mnb<apsyp|&9gDwD`bZCcH>qYra!(%b2XOd4cU<P~$ z&+TvOXwLGMiMdr-`73+#GTv*rN$tFYCjY$Ry6j-|!y}3IWWz+=&msOq6?{i|o=r2# zlZSc`{)BIhWwmH;sn%s-XYD>ysMc?HHb=*kj?*_G%EKWM=7L9LwcVa&%Du+>@#s|a zLS_lWODu#87M~$$XYJGcA*J<;s-g&h((Yu4jqm#1LMVg-INz}MYw2;+e3_^&_kmEB z(=$6ID794R79;0C7B9f*fMQXNti87bn18j<@yfp=))IaJ&wl*RJu^bO{b}QQ(U)b1 z!SG%&Mh$0u4A{aLEO#abbeT=2Ubi6xU^sszd+G~V`stsjUkeH#gsWI!q!~9Ln296P z<^lSgHzCMfc@SbEhS|uC3gC3_md>{w5PXm19Gn?Il1&*>a#dt#htk}c_M=V!>WwE6 zTW9~lvqIR%zCsxM-+W#qONqb2N6Wim-14IuVHe#Y0Bk6z!^=gvM|<F(k2{~W7vOol zFi|fpI3`gSs+aa2Z{1=wRBE3Vy6)4s6cQRV+4;@_Yrzb^SI}k)!!+V#kI~X&`ZQmr z_h$Gr^ILshDrG&-zXW)wr*xDpczP$n-{?FN@|_!tL>kIjs5v<}al@eb<Bb{|=jq)a z@K9~n;FiVGOs$X}VN+4_lEWRLV_#f6Ao=y(hBlng+4L#pi%{o&wq|WF>*z$E%F)<* z+AV;GqMz6!^k*?*LttAiXK_idv|i+s2Tle^dehH?UxbL1(U)*;Ph=R_TmYv+?+Hm0 zf8n8W=j~&QXr40B0t+s&?RSDq6;gThsH5YJBNUD9a((Fx{MT&7So4Hw)>?gp91j&L z#{rb0JW5PN=(7aTz5<0#fadrNTYa>FZ)@wc^r)3ln2Y@ASDPaVCin^!Vv8tv|E&Fr zs-Ua*nW)iV3$t<dm6q~pzB^3tv)YO=_{~xq&CxwDF%oK+4lr$T%*T>rqodPr>d!qU zQHu);w>xE3Gz~1fySqAcZ{o&t9Ce5rB~GiZB6JMD-o6MiLcS7u9+iZpjlG_ety(X; zeq=EJD4<rwVafU%n2Y0$``>96b^!zN!UzIEL52xiJ*qo@Ne6Sh67<!oPsEIS+7fXp z=d>0h2Pd<kIP_@fQ8?rh>I~TMu6KZ){lul`&jvqa{rs4h`v?Gw%LbP>O$LPE2@|+} z3xta9Fb+BeZ9piDSCHZYf=N?6W1l@9AUv7e8pH&MBm4HA{3YO}sWOyzO+YSLmPv-@ zu>yj&QXY&9@KlrU#8v`ffT!ZUK3zoz=ze4<6I%!fFX-X|On_+PN~ao$d;$op`N)s| zhe23GL!wy_wI7uOgXuRD6_Ql<t3xeSpswuNNqvI_&pN7vmr&9<XZN%gArNs@gCTZ~ zk*i4^;}e<;Ytu4jbqzk_s94=JF&lrqWGqL0MY~k{5j-&dVX%{5vK|TSaRN7YqLG*V z0<FJn6ASh0SA5?#ZcK(6tFLEeYqxnxgDRr0D4dPovgQP`a$mkLQn1~?++7(7APzZd zDZ|WR=D<s!(-+||D`A_QdQ=ukQrr1F^8O?^j$*if$b07W-FaQCL5E*|ioGePUfZj@ zxS{Vl74Q-K`GkplJgFQ0v5To=In#c8by2<j%vakw2NJNR&&4ixeMRV+26%ic9m1*E zygPIg&j}U2h;VdJke3!$VXGDmXoOFDN%l3e@9@gZ(f?wbj^}*+LP5NfBYC<|nI_;| zU^B|6HfP8G-YKo1zOX{8R#~p7$Or`yO+@yo{HeBy18b+`q<b|Gwj3Z*f0Q)fm%k;m zf5-o<PIYtig@%3NpCq#v3aLKP>yU#)t<P%e9xVr6IW6!y8og+54MuJ7^%%Caf@oZ8 zlzpFjt#rf^YUVpxhrYMMh8qD@C3qs$hal~k)srzJbH`nZ*RLWx>2)d62M>Hbj_pZF zXPtF<q1??C-9VY^&UzTN!N3B%BG|mo(EJ_s)0!yDcnWbbyIrDokqB3#){Az`R{4c< zng#hA;q8<%KD^RkZ?pEP>2HSXHcjBo&f=d$LGywb%{wk}vdcpb$ed2TwqO<Hlc{?R zqcCxn*74+a06pEO@Fb5)WS|{*IbxE6;U_r{@P*F;x8L<TEGX(utJ6xc6%H2$kaU(q zW{&);3p+^7E@06UaV&Fg6afC~f9X6I0ilIR-jC~_6Q@h`RRR_*Eu3pV{tCc9r8eTX z2Ow-IJlSiG1?uA6IkeRo5EdhIB~k#_HyNX`nu!D20hMOmCLi#uky+WlEr8(85Pfd) zuN^%1i!}ZJ7=(pR)$wetU84&s;B*Ule(u=|SNI>V1Za!aa(-~y@E}g#q(<jO`jZHR z<U@O*3>m9UY0yu!V0<E;7W3SsmA<IQVw`x3xp)*-!Ii;5nv&xF*Y34WIe1FA(C<*7 zo&27g%Vm)JDnpW19f=Ut#hwg!E`i<7SqZ++yW3&y&ClCS#vxyqz3{P!n4@ln-!Qt< z?sXKnt5>@uoG!3!id||KIY8>EWxJdu1&ju3rsA!Vj4#^Kbyweb2;y?ItbOhHV=S-O zVfwOi((!~UO}Dn$Bhd~KQdDPsrO>oTRQ(m=XiIL`BlzLj;XA_7Mco-YzW5IuDA}?t zQ=!e{_JxAH<Q?5nA=6Sr57|IYi2~k1ENryatL51e_V6}dvZk)4G-^ou`@ZClhioWZ z{}K%29kt4-Q3~fhRtb?>GzQ+`h?<(3^T;XnbxQX4AEs}a%$YEdHr9Pf*J%$nO+|G4 zyN7SqW=2%URPIjp+e?iNUs)3p5CbWhM^ZB2;r>qV5LuC@_^|;~*xbx4lF3I~*N48C z<hEB>mq1mH>_CZcv0YzKya4<4QX^%IrsNBC^{JJMfjFv8XEtG%rF>{f2qjDB&Z<ze zz*oDNFukR}r+5%y1&5^mtKy17mYR3<I;JdZ@)wnpho7-v0u29KHvh@B%|1Sz{?HzO zAy0gz$ByC_3|EZ6cozV9LrNxBuXWO(S^KER!A`-u^5?P5<eKiHXIqrsY#Nb_8wG>K z>BSWvW!Z&ntdRIH3zz#>BJ_q^Nppo>#YQ+SC%VEo<aMZw1Z_MKdT$r@?S{NU;z+l1 zL@<P~5R=aCJ&?$W4!3@(05!lq6_U*dw3+WX-tG>J0Q*U=r8Ox2C33KMOFc1AU&D2@ z#j*bqIXcavQ54WUUKv4k6o7)GK>O%i@h_3{apJHs04*#wNU>25cpotMLJCeILRWa8 z!MR?dctx=|0IgpC7t`aSUO)dA$|f!v5JI%#|6-3YQ~LTp6xFYnWLRX%cq@JhOkj;? zR^75qSidB;eL3}H2OkYsw+W0)@vNeqb&VBcylSCs{6=R36}|-cW&IymiZ1IKjZ8O# zY*5fTg7%81rj89W@wSK9zoUhG_q&dGl#3o`3@1Z}?Ga9to}TijsQWL~9Nv@3rqvEO zOnvbXq_brFjw`KEIbX;gMOz>?F>3vG*eZW!l=6E_lHepYL%;qcpBcd}`z#aRk8*ES zMfUbix-6<*5k%Qlmr<FfChDPEOoF&7zZo{>`zblyDeK2DqcWC;CHW?zl^2M@jn_-% z{ZErLwv9>HKE!d_;LoP%?4JEvt<*bFedgq&TR>%t1%>tik?z+8(@Ur3BW2A?4){*x zQukM<mvbaq5?rJs&oW{9s%l7rQhcF*vr*9@Op$J48}{ciO1LBDc#0toHGHoeB;O`h z^wKCV-^O_JmxeX%f6^qb<<hg7m|W($r}hZptYGH#-K8qc5#%+G`EDJRip@OSh9q>0 z1TC*~e8wT(n-FJ9t}7X}IrW^W4ikJG6okCfg8flUgu|HuJk$h`$-BVfSK2|3c7es8 zh+>+1_Y}b744*zA0}w=!a>kYaY&cW8uj(;i!^~;+BOU;0GyIX49snI%a*v&wKL9rD zi$&C_1_&AUxtudZJjXW`4>vA<Iv>msXXaMBR5M4!gSLIA=8@J_&!yt&XyApNC>5Kg z`^`Vy$F%Xm8&HBZ)Ps)rrDJ5#6fLCvVYin#ke;S8a#OwU#HD}Ct+8f7m~Fs}*gB$b z#OEk9(8G^ImfF8?7b78f_{1fh86(ch?yKR;_1#uk@eC}Vt-`S>fkdo4Z`B15^?NLa zr!9(>(U6u^1wVK;4ESAla1y7MXt)Bxdp!|#yS6QR7AVz{OChswwYRJ9>vDEP<p2r1 z9a>$Y>Pp<FizkAS7suUYwI3d%d>(FG-ES)*=2%zEM4@j7aBRL~d#sT$OWwHK+>W-l zjqzg<FG9fR$%oRB-1l}_<ft!)(svt*-Oegplx-fkdXSC+#Qi9h+n>J~5mL>yG~zw_ zlqz)++MXIanVd#x8y)vGGiP&&y%-6W__<Wz@|Do0S{LoVZQr%O*gj0jW(6?+Rz%)P znmizEZd;F2Bq|-B&P7UobcvltrL0OcpIq9towazmTwIQ??*2$Rn=wOqeH6AuU1>SC zdnz{YUz|}q{23XZJiUy|a>1?I{n&TuEv%HgPfKe7fyoGL3Na5T{t9G8jgdk8n`^|5 zdI#HlspkuOkJvjqPuB86sg6%(51d^B0uCP?bo*gTCHENw@=V5)l``$`b&lx+vxY1d zY4%8#><C<gnWx5+_4n17J|qlOV$=1K%L?QC?v2p*B8>J&O{Nn{6|Xim1zO+wIPbr8 zHWKgPKQ}nsB>RY2_gdJhlt~B{E2taQVAE`k7G`A%yFZuy&o~KSmz*6nE*U&=LTYSX z+!9TVewWjm5tvT#_BYQ6A6(r+q`Y3BtM7&mQU<==Nm3UY4CQXRKRL-xFqnDq>@%Hg z7qO&?p_Gr7Bf|Z?u+#y}B*pL9SYHzJU}*ber}qP>1@Tjh&pQ+6+Y_g5M9#@`OncXX zV83UgU)m`4Gvr(m4;w#sy91Ox{cm%<y0V?-<6mOo&y>Sds1J@RW_n;l?e!<?<7W4p zF=k=CF!heZwKLRlinxoD3*u62se3ub{Daen13Z^aw}_5d;(LlbYtpNcg^a|78JCYS z0A<(2jpw1ER941j3%-Xd_FV+~zN>56``0dBnG3tW1=#S((eEDo4+5`E!~Y(ogkmhL ztcoW~-E?+SAmihnO`$Gm_{w=riioP$ZkGkZaROWczsF)uAs<>I;_1h}RvqbPxmoiG zc!&w*rHsu*3kG8J{m)e93Dusj%4%d*^@Hyj0+&xkpa^#G^~2q#qAFgCb;tweC;t6H zfvJO(fP;^Hvw;@U(cQnTC*W`_uaXJY<NGQ|27Q-5tp$1Tiy7=*Yk4oY1qA2xLA*U; z`H%(510*#Ia#wddLBMsUx20Xffk1|j@nOFl2xQfw5;$W#fMD5$%$NqMQ?CfSn*tDc z@I_i`DFHj$Ii(fp1%z-kMK3+Tjtnwrz;@(-z_YwH_6BgBvZ>Hzd_W+_qFyuv!k>kQ zFErf}5PF01#L9@aZZF;zsb#eIk$H~j*%5Vn#B_x{A{)zmg$fOy42HlEhiFGR^J5w? zoZlbe+=%Kxok%E-y&Py0JI*4PbrTqCx;h<JHW6Te45{|RKERDPwIk8&<(41NzVDE( Voi*m+0)P8}l;zdrDrGD}{tE-Ur$+z) diff --git a/xplan-manager/xplan-manager-api/pom.xml b/xplan-manager/xplan-manager-api/pom.xml index 19b21f30c1..0e3712ac3f 100644 --- a/xplan-manager/xplan-manager-api/pom.xml +++ b/xplan-manager/xplan-manager-api/pom.xml @@ -329,6 +329,11 @@ <artifactId>hsqldb</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.skyscreamer</groupId> + <artifactId>jsonassert</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>de.latlon.product.xplanbox</groupId> <artifactId>xplan-core-validator-events</artifactId> 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 b3fc24c434..2dea72d5ae 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 @@ -45,6 +45,7 @@ import de.latlon.xplanbox.api.commons.v1.model.PlanInfoBbox; import de.latlon.xplanbox.api.commons.v1.model.VersionEnum; import de.latlon.xplanbox.api.manager.config.ManagerApiConfiguration; import de.latlon.xplanbox.api.manager.openapi.v1.ApiV1Config; +import de.latlon.xplanbox.api.manager.openapi.v2.ApiV2Config; import de.latlon.xplanbox.api.manager.v1.model.Bereich; import de.latlon.xplanbox.api.manager.v1.model.Gemeinde; import de.latlon.xplanbox.api.manager.v1.model.Link; @@ -131,7 +132,7 @@ public class PlanInfoBuilder { .version(version()) .planStatus(planStatus()) .bbox(bbox()) - .links(links()) + .links(links(ApiV1Config.APP_PATH)) .type(xPlan.getType()) .xplanModelData(xPlanModelData()); } @@ -145,7 +146,7 @@ public class PlanInfoBuilder { .version(version()) .planStatus(planStatus()) .bbox(bbox()) - .links(links()) + .links(links(ApiV2Config.APP_PATH)) .type(xPlan.getType()) .xplanModelData(xPlanModelDataV2()); } @@ -185,9 +186,9 @@ public class PlanInfoBuilder { return VersionEnum.fromValue(xPlan.getVersion()); } - private List<Link> links() { + private List<Link> links(String appPath) { List<Link> links = new ArrayList<>(); - URI selfRef = createSelfRef(); + URI selfRef = createSelfRef(appPath); if (selfRef != null) { Link selfLink = (Link) new Link().rel(SELF).href(selfRef).type(selfMediaType).title(xPlan.getName()); links.add(selfLink); @@ -209,14 +210,14 @@ public class PlanInfoBuilder { return links; } - private URI createSelfRef() { + private URI createSelfRef(String appPath) { URI apiUrl = managerApiConfiguration.getApiUrl(); URIBuilder uriBuilder = new URIBuilder(apiUrl); List<String> pathSegments = new ArrayList<>(); if (apiUrl.getPath() != null && !apiUrl.getPath().isEmpty()) pathSegments.addAll(Arrays.asList(apiUrl.getPath().split("/"))); - pathSegments.addAll(Arrays.asList(ApiV1Config.APP_PATH.split("/"))); + pathSegments.addAll(Arrays.asList(appPath.split("/"))); pathSegments.add("plan"); pathSegments.add(xPlan.getId()); uriBuilder.setPathSegments(pathSegments.stream() diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/AbstractApiConfig.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/AbstractApiConfig.java index 1b6f429a6f..bbe1fef3bd 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/AbstractApiConfig.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/AbstractApiConfig.java @@ -49,9 +49,9 @@ import org.glassfish.jersey.server.ServerProperties; /** * Application configuration for XPlanManager REST API. Example mapping for proxy mapping: * http://xplanbox.lat-lon.de/xmanager/api/v1/ -> - * http://host:8080/xplan-api-manager/xmanager/api/v1/ Public address: + * http://host:8080/xplan-manager-api/xmanager/api/v1/ Public address: * http://xplanbox.lat-lon.de/xmanager/api/v1 Internal address: - * http://host:8080/xplan-api-manager/xmanager/api/v1 + * http://host:8080/xplan-manager-api/xmanager/api/v1 * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v1/ApiV1Config.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v1/ApiV1Config.java index 296df583c2..607bda466d 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v1/ApiV1Config.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v1/ApiV1Config.java @@ -52,9 +52,9 @@ import org.springframework.context.annotation.Configuration; /** * Application configuration for XPlanManager REST API. Example mapping for proxy mapping: * http://xplanbox.lat-lon.de/xmanager/api/v1/ -> - * http://host:8080/xplan-api-manager/xmanager/api/v1/ Public address: + * http://host:8080/xplan-manager-api/xmanager/api/v1/ Public address: * http://xplanbox.lat-lon.de/xmanager/api/v1 Internal address: - * http://host:8080/xplan-api-manager/xmanager/api/v1 + * http://host:8080/xplan-manager-api/xmanager/api/v1 * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> * @since 8.0 diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v2/ApiV2Config.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v2/ApiV2Config.java index fd6c206e5c..6380019f2d 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v2/ApiV2Config.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/openapi/v2/ApiV2Config.java @@ -51,9 +51,9 @@ import org.springframework.context.annotation.Configuration; /** * Application configuration for XPlanManager REST API. Example mapping for proxy mapping: - * http://xplanbox.lat-lon.de/api/v2/ -> http://host:8080/xplan-api-manager/api/v2/ Public + * http://xplanbox.lat-lon.de/api/v2/ -> http://host:8080/xplan-manager-api/api/v2/ Public * address: http://xplanbox.lat-lon.de/api/v2 Internal address: - * http://host:8080/xplan-api-manager/api/v2 + * http://host:8080/xplan-manager-api/api/v2 * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> * @since 8.0 diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlanApi.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlanApi.java index c2bf46f646..6ef844ec0d 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlanApi.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlanApi.java @@ -45,9 +45,7 @@ import static de.latlon.xplanbox.validator.storage.StatusType.IMPORT_REQUESTED; import static de.latlon.xplanbox.validator.storage.StatusType.VALIDATION_FAILED; import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.JSON; 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 org.slf4j.LoggerFactory.getLogger; @@ -62,7 +60,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import de.latlon.core.validator.events.EventSender; import de.latlon.core.validator.events.ValidationRequestedEvent; @@ -138,9 +135,12 @@ public class PlanApi { private static final Logger LOG = getLogger(PlanApi.class); - private final static MediaType[] MEDIA_TYPES_SEARCH = { APPLICATION_JSON_TYPE, XPLANBOX_NO_VERSION_JSON_TYPE, + private final static MediaType[] MEDIA_TYPES_IMPORT = { APPLICATION_JSON_TYPE, XPLANBOX_NO_VERSION_JSON_TYPE, XPLANBOX_V1_JSON_TYPE, XPLANBOX_V2_JSON_TYPE, APPLICATION_XML_TYPE, APPLICATION_ZIP_TYPE }; + private final static MediaType[] MEDIA_TYPES_PLAN_BY_ID = { APPLICATION_JSON_TYPE, APPLICATION_XML_TYPE, + APPLICATION_ZIP_TYPE }; + private static final boolean WITH_SKIP_GEOMETRISCH_VALIDATION = false; private static final boolean WITH_SKIP_RASTER_EVALUATION = false; @@ -281,7 +281,7 @@ public class PlanApi { @PathParam("planId") @Parameter(description = "ID of the plan to be returned", example = "123") String planId) throws Exception { - MediaType requestedMediaType = requestedMediaType(request); + MediaType requestedMediaType = requestedMediaType(request, MEDIA_TYPES_PLAN_BY_ID); if (APPLICATION_ZIP_TYPE.equals(requestedMediaType)) { StreamingOutput plan = planHandler.exportPlan(planId); return Response.ok(plan) @@ -333,14 +333,8 @@ public class PlanApi { example = "bplan_123, fplan-123, rplan20200803") String planName) throws Exception { List<XPlan> plans = planHandler.findPlansByName(planName); - List<PlanInfo> planInfos = plans.stream().map(xPlan -> { - List<Bereich> bereiche = planHandler.findBereiche(xPlan.getId()); - List<Gemeinde> gemeinden = planHandler.findGemeinden(xPlan.getId()); - return new PlanInfoBuilder(xPlan, bereiche, gemeinden, managerApiConfiguration) - .selfMediaType(APPLICATION_JSON) - .alternateMediaType(Arrays.asList(APPLICATION_XML, APPLICATION_ZIP)) - .build(); - }).collect(Collectors.toList()); + MediaType requestedMediaType = requestedMediaType(request, MEDIA_TYPES_PLAN_BY_ID); + List<PlanInfo> planInfos = createPlanInfo(requestedMediaType, plans); return Response.ok().entity(planInfos).build(); } @@ -386,7 +380,7 @@ public class PlanApi { throw new UnexpectedError(status.getErrorMsg()); } List<XPlan> xPlans = planHandler.findPlansById(status.getImportedPlanIds()); - MediaType requestedMediaType = requestedMediaType(request); + MediaType requestedMediaType = requestedMediaType(request, MEDIA_TYPES_IMPORT); if (XPLANBOX_V2_JSON_TYPE.equals(requestedMediaType)) { List<PlanInfo> planInfos = createPlanInfo(requestedMediaType, xPlans); return Response.created(getSelfLink(planInfos)).entity(planInfos).build(); @@ -400,11 +394,16 @@ public class PlanApi { } private List<PlanInfo> createPlanInfo(MediaType requestedMediaType, List<XPlan> plans) { - return plans.stream().map(plan -> createPlanInfo(requestedMediaType, plan)).collect(Collectors.toList()); + if (!APPLICATION_XML_TYPE.equals(requestedMediaType)) + requestedMediaType = APPLICATION_JSON_TYPE; + MediaType finalRequestedMediaType = requestedMediaType; + return plans.stream().map(plan -> createPlanInfo(finalRequestedMediaType, plan)).collect(Collectors.toList()); } private PlanInfo createPlanInfo(MediaType requestedMediaType, XPlan planById) { - List<String> alternateMediaTypes = alternateMediaTypes(requestedMediaType); + if (!APPLICATION_XML_TYPE.equals(requestedMediaType)) + requestedMediaType = APPLICATION_JSON_TYPE; + List<String> alternateMediaTypes = alternateMediaTypes(requestedMediaType, MEDIA_TYPES_PLAN_BY_ID); List<Bereich> bereiche = planHandler.findBereiche(planById.getId()); List<Gemeinde> gemeinden = planHandler.findGemeinden(planById.getId()); return new PlanInfoBuilder(planById, bereiche, gemeinden, managerApiConfiguration) @@ -431,18 +430,18 @@ public class PlanApi { return null; } - private MediaType requestedMediaType(Request request) { - Variant.VariantListBuilder acceptedMediaTypes = Variant.mediaTypes(MEDIA_TYPES_SEARCH); + private MediaType requestedMediaType(Request request, MediaType[] availableMediaTypes) { + Variant.VariantListBuilder acceptedMediaTypes = Variant.mediaTypes(availableMediaTypes); Variant selectVariant = request.selectVariant(acceptedMediaTypes.build()); if (selectVariant == null) return APPLICATION_JSON_TYPE; return selectVariant.getMediaType(); } - private List<String> alternateMediaTypes(MediaType requestedMediaType) { - return Arrays.stream(MEDIA_TYPES_SEARCH) + private List<String> alternateMediaTypes(MediaType requestedMediaType, MediaType[] availableMediaTypes) { + return Arrays.stream(availableMediaTypes) .filter(mediaType -> !mediaType.equals(requestedMediaType)) - .map(mediaType -> mediaType.toString()) + .map(MediaType::toString) .collect(Collectors.toList()); } @@ -487,14 +486,6 @@ public class PlanApi { } } - private List<PlanInfo> createPlanInfoFileFrom(byte[] planInfoAsJsonInByte) throws IOException { - ObjectMapper mapper = new ObjectMapperContextResolver().getContext(PlanInfo.class); - List<de.latlon.xplanbox.api.manager.v2.model.PlanInfo> planInfosV1 = mapper.readValue(planInfoAsJsonInByte, - new TypeReference<List<de.latlon.xplanbox.api.manager.v2.model.PlanInfo>>() { - }); - return PlanInfoBuilder.convertToV1(planInfosV1); - } - private ValidationReport createReportFileFrom(byte[] reportAsJsonInByte) throws IOException { ObjectMapper mapper = new ObjectMapperContextResolver().getContext(ValidationReport.class); return mapper.readValue(reportAsJsonInByte, ValidationReport.class); diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlansApi.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlansApi.java index 741461e65a..d8d8dd176c 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlansApi.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/PlansApi.java @@ -91,6 +91,7 @@ public class PlansApi { List<Gemeinde> gemeinden = planHandler.findGemeinden(xPlan.getId()); return new PlanInfoBuilder(xPlan, bereiche, gemeinden, managerApiConfiguration) .selfMediaType(APPLICATION_JSON) + .alternateMediaType(List.of("application/xml", "application/zip")) .build(); }).collect(Collectors.toList()); return Response.ok().entity(planInfos).build(); diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/PlanApi2.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/PlanApi2.java index 9f6600bff0..5a6ce31ecc 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/PlanApi2.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/PlanApi2.java @@ -26,10 +26,10 @@ import static de.latlon.xplan.commons.util.TextPatternConstants.SIMPLE_NAME_PATT 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.XPlanMediaType.APPLICATION_ZIP; -import static de.latlon.xplanbox.api.commons.XPlanMediaType.APPLICATION_ZIP_TYPE; import static de.latlon.xplanbox.api.commons.v2.model.ResponseLink.RelEnum.STATUS; import static de.latlon.xplanbox.validator.storage.StatusType.IMPORT_REQUESTED; 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.net.URI; @@ -282,7 +282,7 @@ public class PlanApi2 { List<Bereich> bereiche = planHandler.findBereiche(planById.getId()); List<Gemeinde> gemeinden = planHandler.findGemeinden(planById.getId()); return new PlanInfoBuilder(planById, bereiche, gemeinden, managerApiConfiguration) - .selfMediaType(APPLICATION_ZIP_TYPE.toString()) + .selfMediaType(APPLICATION_JSON) .buildV2(); } diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/ReportApi.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/ReportApi.java index a013bcb236..2d37837469 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/ReportApi.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v2/ReportApi.java @@ -21,11 +21,13 @@ package de.latlon.xplanbox.api.manager.v2; import static de.latlon.xplanbox.api.commons.XPlanMediaType.APPLICATION_PDF_TYPE; +import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.GEOJSON; import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.JSON; import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.PDF; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; import de.latlon.xplanbox.api.commons.exception.InvalidValidationUuid; import de.latlon.xplanbox.api.commons.v2.model.ValidationReport; import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; @@ -91,6 +93,55 @@ public class ReportApi { } } + @GET + @Path("/{uuid}/geomfindings") + @Produces({ "application/geo+json" }) + @Operation(summary = "Return ValidationReport", + description = "Returns the findings of the geometrical validation with {uuid} as GeoJSON", + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", + content = @Content(schema = @Schema(implementation = FeatureCollection.class))), + @ApiResponse(responseCode = "404", + description = "Findings of the geometrical validation with {uuid} is not available or is expired"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response geomfindingsByUuid( + @PathParam("uuid") @Parameter(description = "UUID of the validation to be returned", + example = "uuid") String uuid) + throws EventExecutionException, StorageException, InvalidValidationUuid { + return geomfindings(uuid); + } + + @GET + @Path("/{uuid}/geomfindings.json") + @Produces({ "application/geo+json" }) + @Operation(summary = "Return ValidationReport", + description = "Returns the findings of the geometrical validation with {uuid} as GeoJSON", + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", + content = @Content(schema = @Schema(implementation = FeatureCollection.class))), + @ApiResponse(responseCode = "404", + description = "Findings of the geometrical validation with {uuid} is not available or is expired"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response geomfindingsJsonByUuid( + @PathParam("uuid") @Parameter(description = "UUID of the validation to be returned", + example = "uuid") String uuid) + throws EventExecutionException, StorageException, InvalidValidationUuid { + return geomfindings(uuid); + } + + private Response geomfindings(String uuid) throws EventExecutionException, InvalidValidationUuid, StorageException { + try { + byte[] report = validationExecutionStorage.retrieveReport(uuid, GEOJSON); + return Response.ok().entity(report).build(); + } + catch (StorageException e) { + if (e.getStatusCode() == 404) { + throw new InvalidValidationUuid(uuid); + } + throw e; + } + } + private MediaType detectRequestedMediaType(Request request) { Variant.VariantListBuilder acceptedMediaTypes = Variant.mediaTypes(APPLICATION_JSON_TYPE, APPLICATION_PDF_TYPE); Variant selectVariant = request.selectVariant(acceptedMediaTypes.build()); 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 16e0bb6797..4297b6a58a 100644 --- a/xplan-manager/xplan-manager-api/src/main/resources/application.properties +++ b/xplan-manager/xplan-manager-api/src/main/resources/application.properties @@ -1,6 +1,6 @@ ### # #%L -# xplan-api-manager - Modul zur Gruppierung der REST-API +# xplan-manager-api - Modul zur Gruppierung der REST-API # %% # Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH # %% diff --git a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/config/TestContext.java b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/config/TestContext.java index 18f89812d4..8dfb8a5097 100644 --- a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/config/TestContext.java +++ b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/config/TestContext.java @@ -32,12 +32,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.slf4j.LoggerFactory.getLogger; -import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -180,7 +176,7 @@ public class TestContext { @Primary public ManagerApiConfiguration managerApiConfiguration() throws URISyntaxException { ManagerApiConfiguration managerApiConfiguration = mock(ManagerApiConfiguration.class); - when(managerApiConfiguration.getApiUrl()).thenReturn(new URI("http://localhost:8080/xplan-api-manager/")); + when(managerApiConfiguration.getApiUrl()).thenReturn(new URI("http://localhost:8080/xplan-manager-api/")); when(managerApiConfiguration.getDefaultValidationConfiguration()) .thenReturn(new DefaultValidationConfiguration()); return managerApiConfiguration; @@ -221,37 +217,6 @@ public class TestContext { return new XPlanRasterManager(rasterStorage, applicationEventPublisher); } - private void initWorkspace(Path dir) throws IOException, URISyntaxException { - Path themesDir = dir.resolve("themes"); - java.nio.file.Files.createDirectory(themesDir); - Files.copy(Paths.get(getClass().getResource("/bplanraster.xml").toURI()), themesDir.resolve("bplanraster.xml")); - Files.copy(Paths.get(getClass().getResource("/bplanraster.xml").toURI()), - themesDir.resolve("bplanpreraster.xml")); - Files.copy(Paths.get(getClass().getResource("/bplanraster.xml").toURI()), - themesDir.resolve("bplanarchiveraster.xml")); - Files.copy(Paths.get(getClass().getResource("/fplanraster.xml").toURI()), themesDir.resolve("fplanraster.xml")); - Files.copy(Paths.get(getClass().getResource("/fplanraster.xml").toURI()), - themesDir.resolve("fplanpreraster.xml")); - Files.copy(Paths.get(getClass().getResource("/fplanraster.xml").toURI()), - themesDir.resolve("fplanarchiveraster.xml")); - Files.copy(Paths.get(getClass().getResource("/rplanraster.xml").toURI()), themesDir.resolve("rplanraster.xml")); - Files.copy(Paths.get(getClass().getResource("/rplanraster.xml").toURI()), - themesDir.resolve("rplanpreraster.xml")); - Files.copy(Paths.get(getClass().getResource("/rplanraster.xml").toURI()), - themesDir.resolve("rplanarchiveraster.xml")); - Files.copy(Paths.get(getClass().getResource("/lplanraster.xml").toURI()), themesDir.resolve("lplanraster.xml")); - Files.copy(Paths.get(getClass().getResource("/lplanraster.xml").toURI()), - themesDir.resolve("lplanpreraster.xml")); - Files.copy(Paths.get(getClass().getResource("/lplanraster.xml").toURI()), - themesDir.resolve("lplanarchiveraster.xml")); - Files.copy(Paths.get(getClass().getResource("/soplanraster.xml").toURI()), - themesDir.resolve("soplanraster.xml")); - Files.copy(Paths.get(getClass().getResource("/soplanraster.xml").toURI()), - themesDir.resolve("soplanpreraster.xml")); - Files.copy(Paths.get(getClass().getResource("/soplanraster.xml").toURI()), - themesDir.resolve("soplanarchiveraster.xml")); - } - @Bean @Primary public XPlanDao xPlanDao(ManagerWorkspaceWrapper managerWorkspaceWrapper, ManagerConfiguration managerConfiguration) diff --git a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v1/PlanApiTest.java b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v1/PlanApiTest.java index c2ca4e0e67..4bea338c46 100644 --- a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v1/PlanApiTest.java +++ b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v1/PlanApiTest.java @@ -37,6 +37,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import de.latlon.xplanbox.api.manager.ManagerApiJerseyTest; import jakarta.ws.rs.client.Entity; @@ -183,8 +184,9 @@ class PlanApiTest extends ManagerApiJerseyTest<PlanApi> { assertThat(json.from(responseBody)).extractingJsonPathStringValue("$.version").isEqualTo(XPLAN_41.name()); assertThat(json.from(responseBody)).extractingJsonPathStringValue("$.planStatus") .isEqualTo(FESTGESTELLT.name()); - assertThat(json.from(responseBody)).hasJsonPath( - "$.links[?(@.rel=='self' && @.href=='http:\\/\\/localhost:8080\\/xplan-api-manager\\/xmanager\\/api\\/v1\\/plan\\/123')]"); + assertThat(json.from(responseBody)) + .extractingJsonPathArrayValue("$.links[?(@.rel=='self' && @.type=='application/json')].href") + .containsExactlyElementsOf(List.of("http://localhost:8080/xplan-manager-api/xmanager/api/v1/plan/123")); } @Test @@ -198,7 +200,7 @@ class PlanApiTest extends ManagerApiJerseyTest<PlanApi> { XmlAssert.assertThat(responseBody).valueByXPath("/planInfo/planStatus").isEqualTo(FESTGESTELLT.name()); XmlAssert.assertThat(responseBody) .valueByXPath("/planInfo/links[rel='SELF']/href") - .isEqualTo("http://localhost:8080/xplan-api-manager/xmanager/api/v1/plan/123"); + .isEqualTo("http://localhost:8080/xplan-manager-api/xmanager/api/v1/plan/123"); } @Test diff --git a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/PlanApi2Test.java b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/PlanApi2Test.java index fd1c9c28e8..7aa7510b0e 100644 --- a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/PlanApi2Test.java +++ b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/PlanApi2Test.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import de.latlon.xplanbox.api.manager.ManagerApiJerseyTest; import jakarta.ws.rs.client.Entity; @@ -166,8 +167,9 @@ class PlanApi2Test extends ManagerApiJerseyTest<PlanApi2> { assertThat(json.from(responseBody)).extractingJsonPathStringValue("$.version").isEqualTo(XPLAN_41.name()); assertThat(json.from(responseBody)).extractingJsonPathStringValue("$.planStatus") .isEqualTo(FESTGESTELLT.name()); - assertThat(json.from(responseBody)).hasJsonPath( - "$.links[?(@.rel=='self' && @.href=='http:\\/\\/localhost:8080\\/xplan-api-manager\\/xmanager\\/api\\/v1\\/plan\\/123')]"); + assertThat(json.from(responseBody)) + .extractingJsonPathArrayValue("$.links[?(@.rel=='self' && @.type=='application/json')].href") + .containsExactlyElementsOf(List.of("http://localhost:8080/xplan-manager-api/api/v2/plan/123")); } @Test diff --git a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/ReportApiTest.java b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/ReportApiTest.java index f76b73b40f..005a9dd824 100644 --- a/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/ReportApiTest.java +++ b/xplan-manager/xplan-manager-api/src/test/java/de/latlon/xplanbox/api/manager/v2/ReportApiTest.java @@ -20,4 +20,18 @@ public class ReportApiTest extends ManagerApiJerseyTest<ReportApi> { assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } + @Test + void verifyThat_reportGeomfindings_Response_withInvalidUuid() { + final Response response = target("/report/invalidUuid/geomfindings").request().get(); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + + @Test + void verifyThat_reportGeomfindingsJson_Response_withInvalidUuid() { + final Response response = target("/report/invalidUuid/geomfindings.json").request().get(); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + } diff --git a/xplan-manager/xplan-manager-web/src/main/java/de/latlon/xplan/manager/web/server/service/ManagerReportProvider.java b/xplan-manager/xplan-manager-web/src/main/java/de/latlon/xplan/manager/web/server/service/ManagerReportProvider.java index 867e024fb4..f4342d27dc 100644 --- a/xplan-manager/xplan-manager-web/src/main/java/de/latlon/xplan/manager/web/server/service/ManagerReportProvider.java +++ b/xplan-manager/xplan-manager-web/src/main/java/de/latlon/xplan/manager/web/server/service/ManagerReportProvider.java @@ -22,7 +22,7 @@ package de.latlon.xplan.manager.web.server.service; import de.latlon.xplan.validator.report.ReportWriter; import de.latlon.xplanbox.core.gwt.commons.server.service.ReportProvider; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; import org.springframework.beans.factory.annotation.Autowired; import jakarta.servlet.http.HttpServletResponse; @@ -58,7 +58,7 @@ public class ManagerReportProvider implements ReportProvider { @Override public void writeZipReport(HttpServletResponse response, String planUuid, String validationName, - List<ArtifactType> artifacts) throws IOException { + List<ReportFormatType> artifacts) throws IOException { Path planDirectory = planArchiveManager.createReportDirectory(planUuid); reportWriter.writeZipWithArtifacts(response.getOutputStream(), validationName, artifacts, planDirectory); } diff --git a/xplan-tests/xplan-tests-integration/README.md b/xplan-tests/xplan-tests-integration/README.md index 0c81964974..0d13aef372 100644 --- a/xplan-tests/xplan-tests-integration/README.md +++ b/xplan-tests/xplan-tests-integration/README.md @@ -2,6 +2,8 @@ Die Tests in diesem Projekt sind nicht gedacht, um direkt als Teil vom Build ausgeführt zu werden sondern eher zu einem späteren Zeitpunkt, wenn die Anwendungen schon deployt worden sind. +Bei einer lokale Ausführung der Tests muss der Webbrowser "Google Chrome" installiert sein. + ## Ausführung mit Maven ``` diff --git a/xplan-tests/xplan-tests-integration/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java b/xplan-tests/xplan-tests-integration/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java index a0fdbb32ca..31e4eae601 100644 --- a/xplan-tests/xplan-tests-integration/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java +++ b/xplan-tests/xplan-tests-integration/src/test/java/de/latlon/xplanbox/tests/selenium/validatorweb/XPlanValidatorWebIT.java @@ -230,8 +230,8 @@ class XPlanValidatorWebIT { driver.switchTo().frame(iframe); String validationName = driver.findElement(By.xpath("//body/p[1]/b")).getText(); - String planName = driver.findElement(By.xpath("//body/p[7]/b/b")).getText(); - String valide = driver.findElement(By.xpath("//body/b[1]/p[2]/b/font")).getText(); + String planName = driver.findElement(By.xpath("//body/p[7]/b")).getText(); + String valide = driver.findElement(By.xpath("//body/p[9]/b/font")).getText(); assertEquals(expectedValidationName, validationName); assertEquals(expectedPlanName, planName); diff --git a/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-manager-api-soapui-project.xml b/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-manager-api-soapui-project.xml index f975c73e70..94a502271d 100644 --- a/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-manager-api-soapui-project.xml +++ b/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-manager-api-soapui-project.xml @@ -63,6 +63,12 @@ OpenAPI document</con:description> <con:params/> <con:element>data</con:element> </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> <con:request name="Request 1" id="e8223f9a-1c6b-40dd-b28d-ce2681af9b55" mediaType="application/json"> <con:settings/> <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/xmanager/api/v1</con:endpoint> @@ -325,6 +331,42 @@ Import the plan</con:description> <con:mediaType>application/gml+xml</con:mediaType> <con:params/> </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> + <con:representation type="RESPONSE"> + <con:mediaType xsi:nil="true"/> + <con:status>0</con:status> + <con:params/> + <con:element>data</con:element> + </con:representation> <con:request name="Request 1" id="0ab9bfa6-1c76-41df-b253-dabff57568f7" mediaType="application/json" postQueryString="false"> <con:settings/> <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/xmanager/api/v1</con:endpoint> @@ -2210,9 +2252,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="9c116865-1397-455f-9261-af3b7d0f17cb" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -2220,6 +2291,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="9c116865-1397-455f-9261-af3b7d0f17cb" name="link planwerk"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -2402,9 +2482,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="031ac702-0dd7-4ee6-b0a2-af9b263d6af3" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api+json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -2412,6 +2521,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="201ce67b-392b-4c79-b660-bcac1981f760" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -2538,7 +2656,36 @@ assert actualHeader != null</scriptText> </con:assertion> <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api.v2+json')].href</path> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -2546,6 +2693,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="76746657-6e7d-4550-a451-403b37a970e0" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -2727,9 +2883,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="1c5514d8-72bf-4781-87eb-227faffc6315" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api.v1+json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -2737,6 +2922,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="1c5514d8-72bf-4781-87eb-227faffc6315" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -2902,9 +3096,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="abf825ba-2931-4771-aec7-ce9676321275" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api.v2+json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -2912,6 +3135,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="abf825ba-2931-4771-aec7-ce9676321275" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -3140,9 +3372,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="d66e6bfc-96cf-4ab7-9fd1-b3fc16a50325" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -3150,6 +3411,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="d66e6bfc-96cf-4ab7-9fd1-b3fc16a50325" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -3265,9 +3535,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="e1a6634c-04ec-4828-b6b6-abfbdd1868ea" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api+json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -3275,6 +3574,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="e1a6634c-04ec-4828-b6b6-abfbdd1868ea" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -3459,9 +3767,9 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="af4ba2fe-caa2-4617-9f25-5523e3406462" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api+json')].href</path> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -3469,17 +3777,55 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="af4ba2fe-caa2-4617-9f25-5523e3406462" name="link planwerkwms"> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> <con:configuration> - <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/services/planwerkwms/planname/.*$</regEx> + <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:credentials> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="af4ba2fe-caa2-4617-9f25-5523e3406462" name="link planwerkwms"> + <con:configuration> + <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/services/planwerkwms/planname/.*$</regEx> + </con:configuration> + </con:assertion> + <con:credentials> <con:username>${#Project#username}</con:username> <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> @@ -3806,9 +4152,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="6ea52527-2a67-45db-be8c-5c5db7cd4e2e" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api.v2+json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -3816,6 +4191,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="6ea52527-2a67-45db-be8c-5c5db7cd4e2e" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -3936,9 +4320,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="766fb9a5-8bf7-472d-9365-6259e042b62f" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -3946,6 +4359,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="766fb9a5-8bf7-472d-9365-6259e042b62f" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -4116,9 +4538,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="4823fa45-b923-4621-8a26-1cf2777f11d3" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4126,6 +4577,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="4823fa45-b923-4621-8a26-1cf2777f11d3" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -4246,9 +4706,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="c62350b9-98d9-4816-bd38-931acba8f34f" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4256,6 +4745,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="c62350b9-98d9-4816-bd38-931acba8f34f" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -4385,9 +4883,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="9153eaa6-ceea-42c0-a549-1c2eb912c705" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/vnd.xplanbox.api.v2+json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4395,6 +4922,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="9153eaa6-ceea-42c0-a549-1c2eb912c705" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -4510,9 +5046,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="11f6bdb1-6e35-43b9-aaab-19f19bb9fce5" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4520,6 +5085,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="11f6bdb1-6e35-43b9-aaab-19f19bb9fce5" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -4642,9 +5216,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="529a68b9-7584-4c7c-85b3-7a64cef98107" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4652,6 +5255,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="529a68b9-7584-4c7c-85b3-7a64cef98107" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -4772,9 +5384,9 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="4f6d7a6b-5d7f-4341-8e62-26b8afbb0db7" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4782,33 +5394,71 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="4f6d7a6b-5d7f-4341-8e62-26b8afbb0db7" name="link planwerkwms"> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> <con:configuration> - <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/services/planwerkwms/planname/.*$</regEx> + <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:credentials> - <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> - <con:preemptive>true</con:preemptive> - <con:authType>Preemptive</con:authType> - </con:credentials> - <con:attachment> - <con:name>BPlan003_6-0.zip</con:name> - <con:contentType>application/octet-stream</con:contentType> - <con:contentId>BPlan003_6-0.zip</con:contentId> - <con:url>${projectDir}/xplan-manager-api/plans/BPlan003_6-0.zip</con:url> - <con:id>0c70fade-2e59-43eb-a59a-ee6dcbd05e46</con:id> - </con:attachment> - <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="4f6d7a6b-5d7f-4341-8e62-26b8afbb0db7" name="link planwerkwms"> + <con:configuration> + <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/services/planwerkwms/planname/.*$</regEx> + </con:configuration> + </con:assertion> + <con:credentials> + <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> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BPlan003_6-0.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BPlan003_6-0.zip</con:contentId> + <con:url>${projectDir}/xplan-manager-api/plans/BPlan003_6-0.zip</con:url> + <con:id>0c70fade-2e59-43eb-a59a-ee6dcbd05e46</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> <con:jmsPropertyConfig/> <con:parameters> <entry key="skipLaufrichtung" value="" xmlns="http://eviware.com/soapui/config"/> @@ -4902,9 +5552,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="81108919-8476-43a4-ac18-159e41f544e6" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -4912,6 +5591,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="81108919-8476-43a4-ac18-159e41f544e6" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5032,9 +5720,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="dfb288e8-2f67-4e67-b6f9-e513800fd317" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5042,6 +5759,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="dfb288e8-2f67-4e67-b6f9-e513800fd317" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5153,9 +5879,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="9406a6f9-31e9-4e4d-b5bd-fe9114559d7e" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5163,6 +5918,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="9406a6f9-31e9-4e4d-b5bd-fe9114559d7e" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5487,9 +6251,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="bf38df3f-26d1-46dd-a36e-dd867f43d213" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5497,6 +6290,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="bf38df3f-26d1-46dd-a36e-dd867f43d213" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5612,9 +6414,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="0c1db00d-5c3a-4c39-8115-1461a362cdf7" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5622,6 +6453,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="0c1db00d-5c3a-4c39-8115-1461a362cdf7" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5734,9 +6574,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="0d992970-3dec-42a4-8eab-e40496494b23" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5744,6 +6613,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="0d992970-3dec-42a4-8eab-e40496494b23" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -5867,9 +6745,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="5216eb6b-85a7-4122-9389-62bf3bedfcdd" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -5877,6 +6784,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="5216eb6b-85a7-4122-9389-62bf3bedfcdd" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -6008,9 +6924,9 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="76366bea-4f40-4e20-b8dd-9d1a5d4293c1" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -6018,17 +6934,55 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="76366bea-4f40-4e20-b8dd-9d1a5d4293c1" name="link planwerkwms"> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> <con:configuration> - <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/services/planwerkwmspre/planname/.*$</regEx> + <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> - <con:credentials> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="76366bea-4f40-4e20-b8dd-9d1a5d4293c1" name="link planwerkwms"> + <con:configuration> + <path>$.links[?(@.rel == 'planwerkwms')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/services/planwerkwmspre/planname/.*$</regEx> + </con:configuration> + </con:assertion> + <con:credentials> <con:username>${#Project#username}</con:username> <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> @@ -6540,9 +7494,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="16562d56-38a6-4e35-95e1-fdc39fe008fa" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -6550,6 +7533,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="16562d56-38a6-4e35-95e1-fdc39fe008fa" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -6715,9 +7707,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="4a57e07a-a8c7-4341-92cd-ce53cb3a5cc2" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -6725,6 +7746,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="4a57e07a-a8c7-4341-92cd-ce53cb3a5cc2" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -6821,9 +7851,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="a882fdf6-8bd3-4a7e-af73-632f788811b6" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -6831,6 +7890,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="a882fdf6-8bd3-4a7e-af73-632f788811b6" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -6918,9 +7986,38 @@ assert actualHeader != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="6d6a0d2d-70e8-4ede-9ebe-9d543ad40610" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -6928,6 +8025,15 @@ assert actualHeader != null</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="6d6a0d2d-70e8-4ede-9ebe-9d543ad40610" name="link planwerkwms"> <con:configuration> <path>$[0].links[?(@.rel == 'planwerkwms')].href</path> @@ -18072,9 +19178,38 @@ if (documentUrl != "null"){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="8b3c24e4-9eb8-4676-9a4e-57a4c6f3747b" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -18082,6 +19217,15 @@ if (documentUrl != "null"){ <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="8b3c24e4-9eb8-4676-9a4e-57a4c6f3747b" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -18186,6 +19330,42 @@ if (documentUrl != "null"){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> + <con:assertion type="XPath Match" id="319a5f6f-d339-465f-9f40-a0a0ec856134" name="link self count"> + <con:configuration> + <path>count(/planInfo/links[rel='SELF']/href[contains(., '/api/v1/plan/')])</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="XPath Match" id="319a5f6f-d339-465f-9f40-a0a0ec856134" name="link alternate json"> + <con:configuration> + <path>contains(/planInfo/links[rel='ALTERNATE'][type='application/json']/href, '/api/v1/plan/')</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="XPath Match" id="319a5f6f-d339-465f-9f40-a0a0ec856134" name="link alternate zip"> + <con:configuration> + <path>contains(/planInfo/links[rel='ALTERNATE'][type='application/zip']/href, '/api/v1/plan/')</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="XPath Match" id="319a5f6f-d339-465f-9f40-a0a0ec856134" name="link alternate count"> + <con:configuration> + <path>count(/planInfo/links[rel='ALTERNATE']/href[contains(., '/api/v1/plan/')])</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="XPath Match" id="319a5f6f-d339-465f-9f40-a0a0ec856134" name="link planwerkwms"> <con:configuration> <path>contains(/planInfo/links[rel='PLANWERKWMS']/href, '/services/planwerkwms/planname/')</path> @@ -18359,9 +19539,38 @@ assert expectedHeader == actualHeader</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="5d67e227-dc12-474d-887c-a831ad62b6bd" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> + <con:configuration> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate xml"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/xml')].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v1/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link alternate zip"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$.links[?(@.rel == 'alternate' && @.type == 'application/zip')].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> @@ -18369,6 +19578,15 @@ assert expectedHeader == actualHeader</scriptText> <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link alternate count"> + <con:configuration> + <path>$.links[?(@.rel == 'alternate')]</path> + <content>2</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath RegEx Match" id="5d67e227-dc12-474d-887c-a831ad62b6bd" name="link planwerkwms"> <con:configuration> <path>$.links[?(@.rel == 'planwerkwms')].href</path> @@ -21233,64 +22451,100 @@ assert expectedHeader == actualHeader</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /"> + <con:assertion type="JsonPath RegEx Match" id="c7bcd776-0947-4d88-923d-e40eb4bb599c" name="info.version"> <con:configuration> - <path>$.paths./.get.operationId</path> - <content>openApi</content> + <path>$.info.version</path> + <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> + <regEx>2.*</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /report/{uuid}"> + <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /"> <con:configuration> - <path>$.paths./report/{uuid}.get.operationId</path> - <content>reportByUuid</content> + <path>$.paths./.get.operationId</path> + <content>openApi</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="c7bcd776-0947-4d88-923d-e40eb4bb599c" name="version"> + <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /plan response 200 description"> <con:configuration> - <path>$.info.version</path> - <content>true</content> + <path>$.paths./plan.post.responses.200.description</path> + <content>ImportReceipt with uuid of the import</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>2.*</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Existence Match" id="baefa2ef-b925-44ef-b1bb-3e43e1a95a34" name="PlanInfo"> + <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /report/{uuid}"> <con:configuration> - <path>$.components.schemas.PlanInfo</path> - <content>true</content> + <path>$.paths./report/{uuid}.get.operationId</path> + <content>reportByUuid</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Existence Match" id="baefa2ef-b925-44ef-b1bb-3e43e1a95a34" name="ValidationReport"> + <con:assertion type="JsonPath Match" id="2397109c-69bb-40f6-9b5a-2f5db5d69ade" name="path /report/{uuid}/geomfindings"> <con:configuration> - <path>$.components.schemas.ValidationReport</path> - <content>true</content> + <path>$.paths./report/{uuid}/geomfindings.get.operationId</path> + <content>geomfindingsByUuid</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="338faceb-131f-43eb-bf38-2bf035ec149f" name="path /plan response 200 description"> + <con:assertion type="JsonPath Existence Match" id="fc254bba-c565-40fd-be0b-d265e6144a76" name="path /report/{uuid}/geomfindings response 406 exists"> <con:configuration> - <path>$.paths./plan.post.responses.200.description</path> - <content>ImportReceipt with uuid of the import</content> + <path>$.paths./report/{uuid}/geomfindings.get.responses.406</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:assertion type="GroovyScriptAssertion" id="eb77ee0a-79c2-4ea5-a3c0-4bc0011fcd06" name="path /report/{uuid}/geomfindings.json"> + <con:configuration> + <scriptText>import groovy.json.JsonSlurper + +def json = new JsonSlurper().parseText(messageExchange.response.responseContent) + +assert json.paths.'/report/{uuid}/geomfindings.json'.get.operationId == 'geomfindingsJsonByUuid'</scriptText> + </con:configuration> + </con:assertion> + <con:assertion type="GroovyScriptAssertion" id="01846f0c-e87d-4974-9eb6-38ed2e944029" name="path /report/{uuid}/geomfindings.json response 406 exists"> + <con:configuration> + <scriptText>import groovy.json.JsonSlurper + +def json = new JsonSlurper().parseText(messageExchange.response.responseContent) + +assert json.paths.'/report/{uuid}/geomfindings.json'.get.responses.'406'</scriptText> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="baefa2ef-b925-44ef-b1bb-3e43e1a95a34" name="schema PlanInfo"> + <con:configuration> + <path>$.components.schemas.PlanInfo</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="a0fe450e-b235-4d07-bf3b-edd930c011f6" name="schema ValidationReport geomfindings"> + <con:configuration> + <path>$.components.schemas.ValidationReport.properties.geomfindings.$ref</path> + <content>#/components/schemas/FeatureCollection</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> @@ -22145,55 +23399,301 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="groovy" name="pollStatusMismatchingTypes" id="3babb438-a8e0-4474-a305-647b06b616da"> - <con:settings/> - <con:config> - <script>import groovy.json.JsonSlurper - -def testStepName = "GET BP 6.0.2 XX pollStatusMismatchingTypes" -def response = testRunner.testCase.getTestStepByName(testStepName).getPropertyValue("Response") -def json = new JsonSlurper().parseText(response) - -if( context.loopIndex == null ) - context.loopIndex = 0 - -if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ - sleep(1000) - testRunner.gotoStepByName(testStepName) -} else if (context.loopIndex == 40) { - 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 mismatchingContentTypeAndFileType" id="376bae1d-09d7-4f00-803a-cb9da3c4b22b"> + <con:testStep type="groovy" name="pollStatusMismatchingTypes" id="3babb438-a8e0-4474-a305-647b06b616da"> + <con:settings/> + <con:config> + <script>import groovy.json.JsonSlurper + +def testStepName = "GET BP 6.0.2 XX pollStatusMismatchingTypes" +def response = testRunner.testCase.getTestStepByName(testStepName).getPropertyValue("Response") +def json = new JsonSlurper().parseText(response) + +if( context.loopIndex == null ) + context.loopIndex = 0 + +if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ + sleep(1000) + testRunner.gotoStepByName(testStepName) +} else if (context.loopIndex == 40) { + 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 mismatchingContentTypeAndFileType" id="376bae1d-09d7-4f00-803a-cb9da3c4b22b"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX mismatchingContentTypeAndFileType" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/status/${#TestSuite#uuidMismatchingTypes}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4f337b40-5001-46e3-ae48-6c4fcc1920a5" name="status"> + <con:configuration> + <path>$.status</path> + <content>VALIDATION_FAILED</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="c1848a54-240f-4a71-a77d-d352334d6b1f" name="errorMsg"> + <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: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 invalidUuidExpectError404" id="bbb4f23e-2fd0-4cab-856f-8cae8679b1a6"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/status/invalid</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" 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 invalidAcceptHeaderExpectError406" id="6d808e19-6722-4ed2-ae65-8c16efcc698f"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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#baseUrlManagerApi}/xplan-manager-api/api/v2/status/${#TestSuite#uuid}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" 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="67870df1-23e1-4192-889c-1215d60abdef" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{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 retrieveReportJson" id="85410f40-220a-4e1a-9010-c0ea528c6445"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" 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"><entry key="Accept" value="application/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="f1ed37db-538c-4c70-b650-bde8b1c510e2" name="plans.name"> + <con:configuration> + <path>$.plans[?(@.name == 'BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI')].name</path> + <content>[BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="f1ed37db-538c-4c70-b650-bde8b1c510e2" name="plans.type"> + <con:configuration> + <path>$.plans[?(@.name == 'BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI')].type</path> + <content>[BP_Plan]</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 BP 6.0.2 XX retrieveReportPdf" id="e6cae4d0-b1ed-4ebd-980e-1258d6608047"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" 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"><entry key="Accept" value="application/pdf" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="XPath Match" id="e6a3edaf-71fd-4bc3-b441-7ed174a5932c" name="contentType"> + <con:configuration> + <path>/data/@contentType</path> + <content>application/pdf</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 BP 6.0.2 XX invalidUuidExpectError404" id="b62b4284-57e9-49e4-8f2c-b675d679c233"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/invalid</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" 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 invalidAcceptHeaderExpectError406" id="b41034a8-dd5c-4b35-98a9-43ab9b8e2baa"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" 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="9bed2e5b-6a9c-4da6-a527-01affd76103e" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{uuid}/geomfindings TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/report/{uuid}/geomfindings"-Pfad</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveGeomfindings" id="88b564b2-2628-4f72-ac57-5d4ee11976eb"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX mismatchingContentTypeAndFileType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindings" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/status/${#TestSuite#uuidMismatchingTypes}</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}/geomfindings</con:endpoint> <con:request/> - <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>200</codes> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="4f337b40-5001-46e3-ae48-6c4fcc1920a5" name="status"> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="type"> <con:configuration> - <path>$.status</path> - <content>VALIDATION_FAILED</content> + <path>$.type</path> + <content>FeatureCollection</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="c1848a54-240f-4a71-a77d-d352334d6b1f" name="errorMsg"> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="bbox"> <con:configuration> - <path>$.errorMsg</path> - <content>Could not read attached file as XPlanGML</content> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> @@ -22212,15 +23712,15 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidUuidExpectError404" id="bbb4f23e-2fd0-4cab-856f-8cae8679b1a6"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidUuidExpectError404" id="29a869ba-0b72-4035-8d1d-37453b496b54"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidUuidExpectError404" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/status/invalid</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/invalid/geomfindings</con:endpoint> <con:request/> - <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>404</codes> </con:configuration> @@ -22238,15 +23738,15 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" id="6d808e19-6722-4ed2-ae65-8c16efcc698f"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" id="19ca2181-ad13-4ae6-b4fb-4b34de7568b6"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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: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>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/status/${#TestSuite#uuid}</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}/geomfindings</con:endpoint> <con:request/> - <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>406</codes> </con:configuration> @@ -22266,35 +23766,44 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ </con:testStep> <con:properties/> </con:testCase> - <con:testCase id="67870df1-23e1-4192-889c-1215d60abdef" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{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:testCase id="9b6558ff-76b1-4c95-9911-f521141fbcd9" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{uuid}/geomfindings.json TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/report/{uuid}/geomfindings.json"-Pfad</con:description> <con:settings/> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveReportJson" id="85410f40-220a-4e1a-9010-c0ea528c6445"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveGeomfindings" id="81505cf3-1b14-42ef-b175-3484cce434a0"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveReportJson" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindings" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> <con:request/> <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>200</codes> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="f1ed37db-538c-4c70-b650-bde8b1c510e2" name="plans.name"> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="type"> <con:configuration> - <path>$.plans[?(@.name == 'BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI')].name</path> - <content>[BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI]</content> + <path>$.type</path> + <content>FeatureCollection</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Match" id="f1ed37db-538c-4c70-b650-bde8b1c510e2" name="plans.type"> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="bbox"> <con:configuration> - <path>$.plans[?(@.name == 'BPlan_SO-Objekte-Test_6-0-2_SoapUI-XPlanManagerAPI')].type</path> - <content>[BP_Plan]</content> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> @@ -22313,23 +23822,41 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveReportPdf" id="e6cae4d0-b1ed-4ebd-980e-1258d6608047"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveGeomfindingsNoAcceptHeader" id="27a26a43-bd93-40e2-833c-48af5b6d3c7e"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveReportPdf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindingsNoAcceptHeader" 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/pdf" xmlns="http://eviware.com/soapui/config"/></con:setting> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> <con:request/> <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>200</codes> </con:configuration> </con:assertion> - <con:assertion type="XPath Match" id="e6a3edaf-71fd-4bc3-b441-7ed174a5932c" name="contentType"> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="type"> <con:configuration> - <path>/data/@contentType</path> - <content>application/pdf</content> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="bbox"> + <con:configuration> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="d7f432a9-5dd7-46bb-80ad-e8e78f623aa8" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> @@ -22348,15 +23875,15 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidUuidExpectError404" id="b62b4284-57e9-49e4-8f2c-b675d679c233"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidUuidExpectError404" id="c917f544-3f02-4f23-a1f1-7e7835e246e6"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidUuidExpectError404" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/invalid</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/invalid/geomfindings.json</con:endpoint> <con:request/> - <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>404</codes> </con:configuration> @@ -22374,15 +23901,15 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <con:parameters/> </con:config> </con:testStep> - <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" id="b41034a8-dd5c-4b35-98a9-43ab9b8e2baa"> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" id="e24f5ec0-e242-44fd-9516-ed95e0f61ff3"> <con:settings/> - <con:config method="GET" xsi:type="con:HttpRequest" id="d219264b-19a2-4e32-bda8-d1d431e563e9" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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: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>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}</con:endpoint> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> <con:request/> - <con:assertion type="Valid HTTP Status Codes" id="706a255f-d031-4dfe-b078-d7098c7d2b4a" name="Valid HTTP Status Codes"> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> <con:configuration> <codes>406</codes> </con:configuration> @@ -22463,14 +23990,23 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="39a75872-c3c6-4fe9-ac53-858e3008c208" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/api/v1/plan/.*$</regEx> + <regEx>^.*/api/v2/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> <con:assertion type="JsonPath RegEx Match" id="39a75872-c3c6-4fe9-ac53-858e3008c208" name="link planwerkwms"> @@ -22560,14 +24096,23 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="c4de6278-26a0-4499-b07a-a01eb57cafd2" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$[0].links[?(@.rel == 'self' && @.type == 'application/json')].href</path> + <path>$[0].links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> <content>true</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/api/v1/plan/.*$</regEx> + <regEx>^.*/api/v2/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$[0].links[?(@.rel == 'self')]</path> + <content>1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> <con:assertion type="JsonPath RegEx Match" id="c4de6278-26a0-4499-b07a-a01eb57cafd2" name="link planwerkwms"> @@ -26308,14 +27853,23 @@ if( ++context.loopIndex < 40 && json.status != "VALIDATION_FAILED" ){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="ef5b8e76-d4eb-4b91-a20f-cfacbdaaa3d3" name="link self"> + <con:assertion type="JsonPath RegEx Match" id="661feb7b-2379-46aa-a994-f7034df08aaf" name="link self"> <con:configuration> - <path>$.links[?(@.rel == 'self' && @.type == 'application/json')].href</path> - <content>false</content> + <path>$.links[?(@.rel == 'self' && (@.type == 'application/json'))].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>^.*/api/v2/plan/.*$</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Count" id="1f02b841-df99-4f50-bc12-f75834e396e0" name="link self count"> + <con:configuration> + <path>$.links[?(@.rel == 'self')]</path> + <content>1</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>^.*/api/v1/plan/.*$</regEx> </con:configuration> </con:assertion> <con:assertion type="JsonPath RegEx Match" id="ef5b8e76-d4eb-4b91-a20f-cfacbdaaa3d3" name="link planwerkwms"> @@ -28652,6 +30206,78 @@ if( ++context.loopIndex < 40 && json.status != "IMPORT_ABORTED" ){ <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.type"> + <con:configuration> + <path>$.geomfindings.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.type"> + <con:configuration> + <path>$.geomfindings.features[0].type</path> + <content>Feature</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.geometry.type"> + <con:configuration> + <path>$.geomfindings.features[0].geometry.type</path> + <content>Point</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.geometry.coordinates"> + <con:configuration> + <path>$.geomfindings.features[0].geometry.coordinates</path> + <content>[10.04024134282035,53.581840521708514]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.properties.level"> + <con:configuration> + <path>$.geomfindings.features[0].properties.level</path> + <content>ERROR</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.properties.gmlIds"> + <con:configuration> + <path>$.geomfindings.features[0].properties.gmlIds</path> + <content>Gml_C7986975-B702-46BF-9669-E8A2794BCF23</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.properties.planName"> + <con:configuration> + <path>$.geomfindings.features[0].properties.planName</path> + <content>BP_5.2-geometricErrorSelbstueberschneidung_SoapUI-XPlanValidatorAPI</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="geomfindings.features.properties.id"> + <con:configuration> + <path>$.geomfindings.features[0].properties.id</path> + <content>2.2.2.1</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> @@ -28691,6 +30317,104 @@ if( ++context.loopIndex < 40 && json.status != "IMPORT_ABORTED" ){ <con:parameters/> </con:config> </con:testStep> + <con:testStep type="httprequest" name="GET BP 5.2 XX retrieveGeomfindings" id="b95c9898-c9b8-4a47-bcc8-409bad9234c5"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 5.2 XX retrieveGeomfindings" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlManagerApi}/xplan-manager-api/api/v2/report/${#TestCase#uuid}/geomfindings</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:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="type"> + <con:configuration> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.type"> + <con:configuration> + <path>$.features[0].type</path> + <content>Feature</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.geometry.type"> + <con:configuration> + <path>$.features[0].geometry.type</path> + <content>Point</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.geometry.coordinates"> + <con:configuration> + <path>$.features[0].geometry.coordinates</path> + <content>[10.04024134282035,53.581840521708514]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.properties.level"> + <con:configuration> + <path>$.features[0].properties.level</path> + <content>ERROR</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.properties.gmlIds"> + <con:configuration> + <path>$.features[0].properties.gmlIds</path> + <content>Gml_C7986975-B702-46BF-9669-E8A2794BCF23</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.properties.planName"> + <con:configuration> + <path>$.features[0].properties.planName</path> + <content>BP_5.2-geometricErrorSelbstueberschneidung_SoapUI-XPlanValidatorAPI</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="21c11a00-e6f6-48e4-ace1-097d95c9e88a" name="features.properties.id"> + <con:configuration> + <path>$.features[0].properties.id</path> + <content>2.2.2.1</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="transfer" name="CleanUp Properties" id="78e7db8d-3bf3-4b27-b4e3-2237c17b771f"> <con:settings/> <con:config xsi:type="con:PropertyTransfersStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 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 db346ff1e2..6f8ade5e97 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 @@ -4727,6 +4727,16 @@ assert json.version != null</scriptText> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="534d31b6-21c0-44aa-980a-66a47460ddc3" name="info.version"> + <con:configuration> + <path>$.info.version</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>2.*</regEx> + </con:configuration> + </con:assertion> <con:assertion type="JsonPath Match" id="fb838b6f-0eaa-45a1-8a43-f87364021182" name="path /"> <con:configuration> <path>$.paths./.get.operationId</path> @@ -4826,47 +4836,64 @@ assert json.version != null</scriptText> <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:assertion type="JsonPath Match" id="3a711a3a-80f6-4f4b-92d2-412784d41ae8" name="path /report/{uuid}/geomfindings"> <con:configuration> - <path>$.components.schemas.ValidationReport.properties.externalReferences.items.type</path> - <content>string</content> + <path>$.paths./report/{uuid}/geomfindings.get.operationId</path> + <content>geomfindingsByUuid</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:assertion type="JsonPath Existence Match" id="63de7bfa-def9-425f-acb6-d9cd23032741" name="path /report/{uuid}/geomfindings response 406 exists"> <con:configuration> - <path>$.components.schemas.ValidationReport.properties.externalReferencesResult.items.$ref</path> - <content>#/components/schemas/ExternalReferenceResult</content> + <path>$.paths./report/{uuid}/geomfindings.get.responses.406</path> + <content>true</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:assertion type="GroovyScriptAssertion" id="2b33a278-16ba-4b6d-8fbb-c80e74f8debd" name="path /report/{uuid}/geomfindings.json"> <con:configuration> - <path>$.components.schemas.DocumentSummary</path> - <content>true</content> + <scriptText>import groovy.json.JsonSlurper + +def json = new JsonSlurper().parseText(messageExchange.response.responseContent) + +assert json.paths.'/report/{uuid}/geomfindings.json'.get.operationId == 'geomfindingsJsonByUuid'</scriptText> + </con:configuration> + </con:assertion> + <con:assertion type="GroovyScriptAssertion" id="2b33a278-16ba-4b6d-8fbb-c80e74f8debd" name="path /report/{uuid}/geomfindings.json response 406 exists"> + <con:configuration> + <scriptText>import groovy.json.JsonSlurper + +def json = new JsonSlurper().parseText(messageExchange.response.responseContent) + +assert json.paths.'/report/{uuid}/geomfindings.json'.get.responses.'406'</scriptText> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="schemas ValidationReport geomfindings"> + <con:configuration> + <path>$.components.schemas.ValidationReport.properties.geomfindings.$ref</path> + <content>#/components/schemas/FeatureCollection</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> - <con:assertion type="JsonPath RegEx Match" id="534d31b6-21c0-44aa-980a-66a47460ddc3" name="version"> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="schema ValidationReportPlan externalReferencesResult type"> <con:configuration> - <path>$.info.version</path> - <content>true</content> + <path>$.components.schemas.ValidationReportPlan.properties.externalReferencesResult.type</path> + <content>array</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> - <regEx>2.*</regEx> </con:configuration> </con:assertion> - <con:assertion type="JsonPath Existence Match" id="83b62a19-de48-47c5-a3f3-73df0c915dc0" name="ValidationReport"> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="schema ValidationReportPlan externalReferencesResult items ref"> <con:configuration> - <path>$.components.schemas.ValidationReport</path> - <content>true</content> + <path>$.components.schemas.ValidationReportPlan.properties.externalReferencesResult.items.$ref</path> + <content>#/components/schemas/ExternalReferenceResult</content> <allowWildcards>false</allowWildcards> <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> <ignoreComments>false</ignoreComments> @@ -5859,6 +5886,279 @@ if( ++context.loopIndex < 20 && json.status != "VALIDATION_FAILED" ){ </con:testStep> <con:properties/> </con:testCase> + <con:testCase id="8c332f54-4b06-4190-8e8f-2fdcfcb5f0c3" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{uuid}/geomfindings TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/report/{uuid}/geomfindings"-Pfad</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveGeomfindings" id="f74148ad-0208-4eec-8229-eb79062e5bfc"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindings" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/${#TestSuite#uuid}/geomfindings</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="type"> + <con:configuration> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="bbox"> + <con:configuration> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</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 BP 6.0.2 XX invalidUuidExpectError404" id="e1b3b4ae-c475-4e31-9efb-ce62959bd22e"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/invalid/geomfindings</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" 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 invalidAcceptHeaderExpectError406" id="872847f2-0792-4c11-b4cd-53ad289c9948"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/${#TestSuite#uuid}/geomfindings</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" 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="b4f37e2f-56c7-4ca0-9053-a94b3e6f37ad" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/report/{uuid}/geomfindings.json TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/report/{uuid}/geomfindings.json"-Pfad</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveGeomfindings" id="7917db44-c242-4500-b1c0-c45ea61dc62b"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindings" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="type"> + <con:configuration> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="bbox"> + <con:configuration> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</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 BP 6.0.2 XX retrieveGeomfindingsNoAcceptHeader" id="4446c502-27a2-4e1d-80ff-317778aa75f7"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX retrieveGeomfindingsNoAcceptHeader" 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/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="type"> + <con:configuration> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="bbox"> + <con:configuration> + <path>$.bbox</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="4c25065a-9120-401a-a1bc-06c0129e9b8c" name="features"> + <con:configuration> + <path>$.features</path> + <content>[]</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 BP 6.0.2 XX invalidUuidExpectError404" id="d66a2ea2-9fde-4332-8368-2e11c890b61e"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidUuidExpectError404" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/invalid/geomfindings.json</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" 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 invalidAcceptHeaderExpectError406" id="09e765bf-23e9-42ba-89fc-b1908b80e479"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="873abd32-15a2-4f63-a5cf-93aa57bee711" name="GET BP 6.0.2 XX invalidAcceptHeaderExpectError406" 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/json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/${#TestSuite#uuid}/geomfindings.json</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="2b121572-75dc-4331-8608-63651d9942fe" 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/> @@ -7541,6 +7841,78 @@ if( ++context.loopIndex < 20 && json.status != "VALIDATION_FINISHED" <ignoreComments>false</ignoreComments> </con:configuration> </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.type"> + <con:configuration> + <path>$.geomfindings.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.type"> + <con:configuration> + <path>$.geomfindings.features[0].type</path> + <content>Feature</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.geometry.type"> + <con:configuration> + <path>$.geomfindings.features[0].geometry.type</path> + <content>Point</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.geometry.coordinates"> + <con:configuration> + <path>$.geomfindings.features[0].geometry.coordinates</path> + <content>[10.04024134282035,53.581840521708514]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.properties.level"> + <con:configuration> + <path>$.geomfindings.features[0].properties.level</path> + <content>ERROR</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.properties.gmlIds"> + <con:configuration> + <path>$.geomfindings.features[0].properties.gmlIds</path> + <content>Gml_C7986975-B702-46BF-9669-E8A2794BCF23</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.properties.planName"> + <con:configuration> + <path>$.geomfindings.features[0].properties.planName</path> + <content>BP_5.2-geometricErrorSelbstueberschneidung_SoapUI-XPlanValidatorAPI</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="geomfindings.features.properties.id"> + <con:configuration> + <path>$.geomfindings.features[0].properties.id</path> + <content>2.2.2.1</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> @@ -7580,6 +7952,104 @@ if( ++context.loopIndex < 20 && json.status != "VALIDATION_FINISHED" <con:parameters/> </con:config> </con:testStep> + <con:testStep type="httprequest" name="GET BP 5.2 XX retrieveGeomfindings" id="674b4627-509a-4b9e-86d8-b896b864e983"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 5.2 XX retrieveGeomfindings" 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/geo+json" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/report/${#TestCase#uuid}/geomfindings</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:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="type"> + <con:configuration> + <path>$.type</path> + <content>FeatureCollection</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.type"> + <con:configuration> + <path>$.features[0].type</path> + <content>Feature</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.geometry.type"> + <con:configuration> + <path>$.features[0].geometry.type</path> + <content>Point</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.geometry.coordinates"> + <con:configuration> + <path>$.features[0].geometry.coordinates</path> + <content>[10.04024134282035,53.581840521708514]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.properties.level"> + <con:configuration> + <path>$.features[0].properties.level</path> + <content>ERROR</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.properties.gmlIds"> + <con:configuration> + <path>$.features[0].properties.gmlIds</path> + <content>Gml_C7986975-B702-46BF-9669-E8A2794BCF23</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.properties.planName"> + <con:configuration> + <path>$.features[0].properties.planName</path> + <content>BP_5.2-geometricErrorSelbstueberschneidung_SoapUI-XPlanValidatorAPI</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fba6cda7-8de7-4745-be6a-53a6dd7db58b" name="features.properties.id"> + <con:configuration> + <path>$.features[0].properties.id</path> + <content>2.2.2.1</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="transfer" name="CleanUp Properties" id="05519fd5-c866-4cce-8735-48bf8660526d"> <con:settings/> <con:config xsi:type="con:PropertyTransfersStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ReportApi.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ReportApi.java index 781c68e365..6b51de1694 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ReportApi.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ReportApi.java @@ -21,11 +21,13 @@ package de.latlon.xplanbox.api.validator.v2; import static de.latlon.xplanbox.api.commons.XPlanMediaType.APPLICATION_PDF_TYPE; +import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.GEOJSON; import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.JSON; import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType.PDF; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; import de.latlon.xplanbox.api.commons.exception.InvalidValidationUuid; import de.latlon.xplanbox.api.commons.v2.model.ValidationReport; import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; @@ -88,6 +90,55 @@ public class ReportApi { } } + @GET + @Path("/{uuid}/geomfindings") + @Produces({ "application/geo+json" }) + @Operation(summary = "Return ValidationReport", + description = "Returns the findings of the geometrical validation with {uuid} as GeoJSON", + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", + content = @Content(schema = @Schema(implementation = FeatureCollection.class))), + @ApiResponse(responseCode = "404", + description = "Findings of the geometrical validation with {uuid} is not available or is expired"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response geomfindingsByUuid( + @PathParam("uuid") @Parameter(description = "UUID of the validation to be returned", + example = "uuid") String uuid) + throws EventExecutionException, StorageException, InvalidValidationUuid { + return geomfindings(uuid); + } + + @GET + @Path("/{uuid}/geomfindings.json") + @Produces({ "application/geo+json" }) + @Operation(summary = "Return ValidationReport", + description = "Returns the findings of the geometrical validation with {uuid} as GeoJSON", + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", + content = @Content(schema = @Schema(implementation = FeatureCollection.class))), + @ApiResponse(responseCode = "404", + description = "Findings of the geometrical validation with {uuid} is not available or is expired"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response geomfindingsJsonByUuid( + @PathParam("uuid") @Parameter(description = "UUID of the validation to be returned", + example = "uuid") String uuid) + throws EventExecutionException, StorageException, InvalidValidationUuid { + return geomfindings(uuid); + } + + private Response geomfindings(String uuid) throws EventExecutionException, InvalidValidationUuid, StorageException { + try { + byte[] report = validationExecutionStorage.retrieveReport(uuid, GEOJSON); + return Response.ok().entity(report).build(); + } + catch (StorageException e) { + if (e.getStatusCode() == 404) { + throw new InvalidValidationUuid(uuid); + } + throw e; + } + } + private MediaType detectRequestedMediaType(Request request) { Variant.VariantListBuilder acceptedMediaTypes = Variant.mediaTypes(APPLICATION_JSON_TYPE, APPLICATION_PDF_TYPE); Variant selectVariant = request.selectVariant(acceptedMediaTypes.build()); 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 index 4a5c1cf2dc..c7da99f078 100644 --- 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 @@ -74,7 +74,8 @@ class DefaultApi2Test extends ValidatorApiJerseyTest { BasicJsonTester json = new BasicJsonTester(getClass()); assertThat(json.from(response)).extractingJsonPathMapValue("$.paths") - .containsOnlyKeys("/", "/info", "/status/{uuid}", "/validate", "/report/{uuid}"); + .containsOnlyKeys("/", "/info", "/status/{uuid}", "/validate", "/report/{uuid}", + "/report/{uuid}/geomfindings", "/report/{uuid}/geomfindings.json"); } @Test diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ReportApiTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ReportApiTest.java index ac9d23aa93..8ffe2149b9 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ReportApiTest.java +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ReportApiTest.java @@ -24,10 +24,24 @@ public class ReportApiTest extends ValidatorApiJerseyTest { } @Test - void verifyThat_Response_withInvalidUuid() { + void verifyThat_report_Response_withInvalidUuid() { final Response response = target("/report/invalidUuid").request(APPLICATION_JSON).get(); assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } + @Test + void verifyThat_reportGeomfindings_Response_withInvalidUuid() { + final Response response = target("/report/invalidUuid/geomfindings").request().get(); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + + @Test + void verifyThat_reportGeomfindingsJson_Response_withInvalidUuid() { + final Response response = target("/report/invalidUuid/geomfindings.json").request().get(); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + } diff --git a/xplan-validator/xplan-validator-executor/pom.xml b/xplan-validator/xplan-validator-executor/pom.xml index f4a22182cc..a36c3b9cf7 100644 --- a/xplan-validator/xplan-validator-executor/pom.xml +++ b/xplan-validator/xplan-validator-executor/pom.xml @@ -209,7 +209,7 @@ <id>docker</id> <properties> <docker-image.skip>false</docker-image.skip> - <docker-contextTarFile.expectedSizeInMat10pct>75</docker-contextTarFile.expectedSizeInMat10pct> + <docker-contextTarFile.expectedSizeInMat10pct>84</docker-contextTarFile.expectedSizeInMat10pct> </properties> <dependencies> <dependency> <!-- to copy jmx exporter stuff from docker image --> @@ -231,4 +231,4 @@ </profile> </profiles> -</project> \ No newline at end of file +</project> 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 index 24b569c77b..d0f0e8b322 100644 --- 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 @@ -1,5 +1,6 @@ package de.latlon.xplanbox.validator.executor; +import static de.latlon.xplan.validator.report.geojson.GeoJsonBuilder.createGeoJsonFailures; import static de.latlon.xplanbox.validator.storage.StatusType.VALIDATION_FAILED; import static de.latlon.xplanbox.validator.storage.exception.ErrorType.INTERNAL_ERROR; import static de.latlon.xplanbox.validator.storage.exception.ErrorType.INVALID_REQUEST; @@ -19,7 +20,9 @@ import de.latlon.core.validator.events.v1.XPlanPublicV1Event; import de.latlon.core.validator.events.v1.XPlanPublicV1Event.EventType; import de.latlon.xplan.commons.archive.XPlanArchive; import de.latlon.xplan.validator.ValidatorException; +import de.latlon.xplan.validator.report.ReportGenerationException; import de.latlon.xplan.validator.report.ValidatorReport; +import de.latlon.xplan.validator.report.geojson.model.FeatureCollection; import de.latlon.xplanbox.api.commons.ObjectMapperContextResolver; import de.latlon.xplanbox.api.commons.ValidationReportBuilder; import de.latlon.xplanbox.api.commons.exception.XPlanApiException; @@ -109,6 +112,7 @@ public class PlanValidator { ValidationReport validationReport = createValidationReport(event, archive, validatorReport); reports.put(ReportType.JSON, createJsonReportFile(validationReport)); + reports.put(ReportType.GEOJSON, createGeoJsonReportFile(validatorReport)); reports.put(ReportType.PDF, createPdfReportFile(validatorReport)); reports.put(ReportType.ZIP, createZipReportFile(validatorReport)); @@ -146,7 +150,7 @@ public class PlanValidator { } private ValidationReport createValidationReport(ValidationRequestedEvent event, final XPlanArchive archive, - ValidatorReport validatorReport) { + ValidatorReport validatorReport) throws ReportGenerationException { URI wmsUrl = validationHandler.addToWms(archive); return new ValidationReportBuilder().validatorReport(validatorReport) // .filename(event.getxFileName()) // @@ -161,6 +165,17 @@ public class PlanValidator { return reportFile; } + private Path createGeoJsonReportFile(ValidatorReport validatorReport) + throws IOException, ReportGenerationException { + FeatureCollection geoJsonFailures = createGeoJsonFailures(validatorReport); + if (geoJsonFailures == null) + geoJsonFailures = new FeatureCollection(); + ObjectMapper mapper = new ObjectMapperContextResolver().getContext(FeatureCollection.class); + Path reportFile = Files.createTempFile("", ".geojson"); + mapper.writeValue(reportFile.toFile(), geoJsonFailures); + return reportFile; + } + private Path createPdfReportFile(ValidatorReport validatorReport) throws IOException { return validationHandler.writePdfReport(validatorReport); } diff --git a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java index a7c511a257..50f6df0efd 100644 --- a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java @@ -20,7 +20,8 @@ */ package de.latlon.xplanbox.validator.executor.handler; -import static de.latlon.xplan.validator.web.shared.ArtifactType.PDF; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.GEOJSON; +import static de.latlon.xplan.validator.web.shared.ReportFormatType.PDF; import javax.xml.stream.XMLStreamException; import java.io.File; @@ -44,7 +45,7 @@ import de.latlon.xplan.validator.XPlanValidator; import de.latlon.xplan.validator.configuration.ValidatorConfiguration; import de.latlon.xplan.validator.report.ReportWriter; import de.latlon.xplan.validator.report.ValidatorReport; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; import de.latlon.xplan.validator.web.shared.ValidationSettings; import de.latlon.xplan.validator.wms.ValidatorWmsManager; import de.latlon.xplanbox.api.commons.exception.InvalidXPlanGmlOrArchive; @@ -94,7 +95,7 @@ public class ValidationHandler { LOG.debug("Create zip report in directory {} with validationName {}", workDir, validationName); reportWriter.writeArtefacts(validatorReport, workDir); - List<ArtifactType> artifacts = Arrays.asList(PDF); + List<ReportFormatType> artifacts = Arrays.asList(PDF, GEOJSON); Path zipArchive = workDir.resolve(validationName + ".zip"); try (OutputStream zipOutput = Files.newOutputStream(zipArchive)) { 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 index 603c6f2b27..f7dcea424e 100644 --- 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 @@ -1,1224 +1,1458 @@ { - "date": "-fixed-for-comparison-", - "filename": null, - "syntaktisch": { - "valid": true, - "status": "COMPLETED" - }, - "validationName": "edfd613e-b85d-4ea6-9b97-bb33712b1ba6", - "plans": [ - { - "valid": false, - "externalReferencesResult": [], - "rasterEvaluationResult": [], - "validationResult": { - "semantisch": { - "valid": true, - "rulesMetadata": { - "source": "https:\/\/gitlab.opencode.de\/xleitstelle\/xplanung\/validierungsregeln\/standard\/-\/tree\/v1.1.9", - "version": "1.1.9" - }, - "rules": [ - { - "id": "2.1.2.1", - "title": "Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType" - }, - { - "id": "2.1.3.1", - "title": "Angabe eines Standard CRS" - }, - { - "id": "2.2.1.1", - "title": "Flächenschlussbedingung" - }, - { - "id": "3.1.1.1", - "title": "Relationen auf Text-Abschnitte" - }, - { - "id": "3.1.1.2", - "title": "Relationen auf Begründungs-Abschnitte" - }, - { - "id": "3.1.2.1", - "title": "Relation auf Präsentationsobjekte" - }, - { - "id": "3.1.2.2", - "title": "Relation auf Fachobjekte" - }, - { - "id": "3.1.2.3", - "title": "Relation auf Basis-Rasterplan" - }, - { - "id": "3.1.3.1", - "title": "Relationen auf Text-Abschnitte" - }, - { - "id": "3.1.3.2", - "title": "Relationen auf Begründungs-Abschnitte" - }, - { - "id": "3.1.3.3", - "title": "Rückwärts-Referenzen auf Plan-Bereiche" - }, - { - "id": "3.1.3.4", - "title": "Rückwärts-Referenzen auf Präsentationsobjekte" - }, - { - "id": "3.2.1.1", - "title": "Spezifikation des Textinhalts" - }, - { - "id": "3.2.2.1", - "title": "Spezifikation des Textinhalts" - }, - { - "id": "3.2.3.1", - "title": "Konsistenz der verschiedenen Höhenangaben" - }, - { - "id": "3.2.3.2", - "title": "Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist" - }, - { - "id": "3.2.3.3", - "title": "Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug" - }, - { - "id": "3.2.4.1", - "title": "Verweis auf Dokumente." - }, - { - "id": "3.2.5.1", - "title": "Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel)." - }, - { - "id": "3.3.1.1", - "title": "Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten" - }, - { - "id": "3.3.1.2", - "title": "Konsistenz der Attribute art und index" - }, - { - "id": "3.3.1.3", - "title": "Rückwärts-Referenzen auf Plan-Bereiche" - }, - { - "id": "3.3.2.1", - "title": "Einschränkung des Raumbezugs auf Punktgeometrie." - }, - { - "id": "3.3.3.1", - "title": "Einschränkung des Raumbezugs auf Liniengeometrie." - }, - { - "id": "3.3.4.1", - "title": "Einschränkung des Raumbezugs auf Flächengeometrie." - }, - { - "id": "4.1.1.1", - "title": "Einschränkung der Relation bereich" - }, - { - "id": "4.1.2.1", - "title": "Einschränkung der Relation inhaltBPlan" - }, - { - "id": "4.1.2.2", - "title": "Einschränkung der Relation rasterAenderung" - }, - { - "id": "4.1.2.3", - "title": "Einschränkung der Relation gehoertZuPlan" - }, - { - "id": "4.1.3.1", - "title": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche" - }, - { - "id": "4.1.3.2", - "title": "Einschränkung der Relation wirdAusgeglichenDurchMassnahme" - }, - { - "id": "4.1.3.3", - "title": "Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme" - }, - { - "id": "4.1.3.4", - "title": "Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche" - }, - { - "id": "4.1.3.5", - "title": "Rückwärts-Referenzen auf Plan-Bereiche" - }, - { - "id": "4.1.3.6", - "title": "BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." - }, - { - "id": "4.1.3.7", - "title": "Einschränkung der Relation wirdAusgeglichenDurchABE" - }, - { - "id": "4.1.4.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "4.1.4.2", - "title": "Einschränkung auf Flächengeometrie" - }, - { - "id": "4.1.5.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "4.1.5.2", - "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" - }, - { - "id": "4.1.6.1", - "title": "Ãœberlagerungsobjekte gehören nie zum Flächenschluss." - }, - { - "id": "4.1.7.1", - "title": "Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss" - }, - { - "id": "4.1.8.1", - "title": "Einschränkung auf Liniengeometrie" - }, - { - "id": "4.1.9.1", - "title": "Einschränkung auf Punktgeometrie" - }, - { - "id": "4.2.1", - "title": "Konsistenz der Angaben zur GFZ" - }, - { - "id": "4.2.2", - "title": "Konsistenz der Angaben zur GF" - }, - { - "id": "4.2.3", - "title": "Konsistenz der Angaben zur GFZ und GF" - }, - { - "id": "4.2.4", - "title": "Konsistenz der Angaben zur BMZ" - }, - { - "id": "4.2.5", - "title": "Konsistenz der Angaben zur BM" - }, - { - "id": "4.2.6", - "title": "Konsistenz der Angaben zur BMZ und BM" - }, - { - "id": "4.2.7", - "title": "Konsistenz der Angaben zur GRZ" - }, - { - "id": "4.2.8", - "title": "Konsistenz der Angaben zur GR" - }, - { - "id": "4.2.9", - "title": "Konsistenz der Angaben zur GRZ und GR" - }, - { - "id": "4.2.10", - "title": "Konsistenz der Angaben zu Z" - }, - { - "id": "4.2.11", - "title": "Konsistenz der Angaben zu ZU" - }, - { - "id": "4.3.1", - "title": "Konsistenz der Angaben zur Dachneigung" - }, - { - "id": "4.3.2", - "title": "Konsistenz der Attribute dachform und detaillierteDachform" - }, - { - "id": "4.5.1.1", - "title": "Relation abweichungText" - }, - { - "id": "4.5.1.2", - "title": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung" - }, - { - "id": "4.5.1.3", - "title": "Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung" - }, - { - "id": "4.5.1.4", - "title": "Konsistenz der Attribute bauweise und abweichendeBauweise" - }, - { - "id": "4.5.1.5", - "title": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung" - }, - { - "id": "4.5.2.1", - "title": "Relation baugrenze" - }, - { - "id": "4.5.2.2", - "title": "Relation baulinie" - }, - { - "id": "4.5.10.1", - "title": "Konsistenz der Attribute typ und sonstTyp" - }, - { - "id": "4.5.13.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.5.13.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.5.13.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.5.14.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.5.14.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.5.14.3", - "title": "Einschränkung der Relation eigentümer" - }, - { - "id": "4.5.14.4", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.5.15.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.6.3.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.7.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.7.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "4.7.1.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.7.1.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "4.7.1.5", - "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)" - }, - { - "id": "4.7.1.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "4.7.2.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.7.2.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.7.2.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.8.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.8.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.8.1.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.8.2.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.8.2.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.8.2.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.8.2.4", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.8.3.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.8.3.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "4.8.3.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.8.3.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "4.8.3.5", - "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi" - }, - { - "id": "4.8.3.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "4.9.2.1", - "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" - }, - { - "id": "4.9.2.2", - "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" - }, - { - "id": "4.9.2.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" - }, - { - "id": "4.9.3.1", - "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" - }, - { - "id": "4.9.3.2", - "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" - }, - { - "id": "4.9.3.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" - }, - { - "id": "4.9.5.1", - "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" - }, - { - "id": "4.9.5.2", - "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" - }, - { - "id": "4.9.5.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" - }, - { - "id": "4.9.5.4", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.9.6.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.10.2.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.11.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.11.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "4.11.1.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "4.11.1.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "4.11.1.5", - "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi" - }, - { - "id": "4.11.1.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "4.11.1.7", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.12.1.1", - "title": "Einschränkung der Relation begrenzungslinie" - }, - { - "id": "4.12.2.1", - "title": "Einschränkung der Relation begrenzungslinie" - }, - { - "id": "4.12.2.2", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "4.12.3.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.13.1.1", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "4.13.2.1", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "4.14.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.14.2.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "4.14.3.1", - "title": "Konsistenz der Attribute startWinkel und endWinkel" - }, - { - "id": "4.14.3.2", - "title": "Kein flächenhafter Raumbezug" - }, - { - "id": "4.14.4.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "4.14.6.1", - "title": "Notwendige Spezifikation einer Textlichen Darstellung" - }, - { - "id": "4.14.7.1", - "title": "Verwendung der Relation hoehenangabe" - }, - { - "id": "4.14.7.2", - "title": "Kein flächenhafter Raumbezug" - }, - { - "id": "5.1.1.1", - "title": "Einschränkung der Relation bereich" - }, - { - "id": "5.1.1.2", - "title": "Konsistenz der Attribute planArt und sonstPlanArt" - }, - { - "id": "5.1.2.1", - "title": "Einschränkung der Relation inhaltFPlan" - }, - { - "id": "5.1.2.2", - "title": "Einschränkung der Relation rasterAenderung" - }, - { - "id": "5.1.2.3", - "title": "Einschränkung der Relation gehoertZuPlan" - }, - { - "id": "5.1.3.1", - "title": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche" - }, - { - "id": "5.1.3.2", - "title": "Einschränkung der Relation wirdAusgeglichenDurchSPE" - }, - { - "id": "5.1.3.3", - "title": "Rückwärts-Referenzen auf Plan-Bereiche" - }, - { - "id": "5.1.3.4", - "title": "FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." - }, - { - "id": "5.1.4.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "5.1.4.2", - "title": "Einschränkung auf Flächengeometrie" - }, - { - "id": "5.1.5.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "5.1.5.2", - "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" - }, - { - "id": "5.1.6.1", - "title": "Ãœberlagerungsobjekte gehören nie zum Flächenschluss" - }, - { - "id": "5.1.7.1", - "title": "Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss" - }, - { - "id": "5.1.8.1", - "title": "Einschränkung auf Liniengeometrie" - }, - { - "id": "5.1.9.1", - "title": "Einschränkung auf Punktgeometrie" - }, - { - "id": "5.3.1.1", - "title": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung" - }, - { - "id": "5.3.1.2", - "title": "Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung" - }, - { - "id": "5.3.1.3", - "title": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung" - }, - { - "id": "5.3.1.4", - "title": "Konsistenz der Angaben zur GFZ" - }, - { - "id": "5.4.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.4.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "5.4.1.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "5.4.1.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "5.4.1.5", - "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)" - }, - { - "id": "5.4.1.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "5.4.1.7", - "title": "Einschränkung des Raumbezugs" - }, - { - "id": "5.4.1.8", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.4.2.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.4.2.2", - "title": "Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung" - }, - { - "id": "5.4.2.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "5.4.2.4", - "title": "Einschränkung des Raumbezugs" - }, - { - "id": "5.4.2.5", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.5.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.5.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "5.5.1.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "5.5.2.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.5.2.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "5.5.2.3", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "5.5.3.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.5.3.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "5.5.3.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "5.5.3.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "5.5.3.5", - "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)" - }, - { - "id": "5.5.3.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "5.5.3.7", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.6.1.1", - "title": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2" - }, - { - "id": "5.6.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" - }, - { - "id": "5.6.2.1", - "title": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2" - }, - { - "id": "5.6.2.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" - }, - { - "id": "5.7.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.7.1.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "5.7.1.3", - "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" - }, - { - "id": "5.7.1.4", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "5.7.1.5", - "title": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)" - }, - { - "id": "5.7.1.6", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "5.7.1.7", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.8.1.1", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "5.8.1.2", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" - }, - { - "id": "5.8.1.3", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.9.1.1", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "5.9.1.2", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.9.2.1", - "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" - }, - { - "id": "5.9.2.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.10.1.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.10.2.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.10.2.2", - "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" - }, - { - "id": "5.10.2.3", - "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" - }, - { - "id": "5.10.2.4", - "title": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)" - }, - { - "id": "5.10.3.1", - "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" - }, - { - "id": "5.10.4.1", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "5.10.6.1", - "title": "Notwendige Spezifikation einer Textlichen Darstellung" - }, - { - "id": "6.1.1.1", - "title": "Einschränkung der Relation bereich" - }, - { - "id": "6.1.2.1", - "title": "Einschränkung der Relation inhaltSoPlan" - }, - { - "id": "6.1.2.2", - "title": "Einschränkung der Relation rasterAenderung" - }, - { - "id": "6.1.2.3", - "title": "Einschränkung der Relation gehoertZuPlan" - }, - { - "id": "6.1.3.1", - "title": "Rückwärts-Referenzen auf Plan-Bereiche" - }, - { - "id": "6.1.3.2", - "title": "SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." - }, - { - "id": "6.1.4.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "6.1.4.2", - "title": "Einschränkung auf Flächengeometrie" - }, - { - "id": "6.1.5.1", - "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" - }, - { - "id": "6.1.5.2", - "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" - }, - { - "id": "6.1.6.1", - "title": "Einschränkung auf Liniengeometrie" - }, - { - "id": "6.1.7.1", - "title": "Einschränkung auf Punktgeometrie" - }, - { - "id": "6.2.1.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.1.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.2.2.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.2.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.2.3.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.4.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.5.1", - "title": "Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung" - }, - { - "id": "6.2.5.2", - "title": "Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.5.3", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.2.6.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.7.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.2.7.2", - "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.2.8.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.3.1.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.3.1.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.3.2.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.3.2.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.3.3.1", - "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" - }, - { - "id": "6.3.3.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.4.1.1", - "title": "Konsistenz der Attribute gebietsArt und sonstGebietsArt" - }, - { - "id": "6.4.1.2", - "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" - }, - { - "id": "6.5.1.1", - "title": "Konsistenz von typ und sonstTyp" - } - ], - "status": "COMPLETED" - }, - "geometrisch": { - "valid": false, - "rules": [ - { - "id": "2.2.1.1", - "title": "Flächenschlussbedingung" - }, - { - "id": "2.2.3.1", - "title": "Raumbezogene Objekte im Innern des Geltungsbereichs" - }, - { - "findings": [ - { - "markerGeom": { - "coordinates": [ - [ - 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 - ] - ], - "type": "LineString" - }, - "level": "ERROR", - "gmlIds": [ - "GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3" - ], - "message": "äußerer Ring verwendet falsche Laufrichtung (CW)." - } - ], - "id": "2.2.2.1", - "title": "Verwendung geometrisch korrekter Flächen" - } + "date": "-fixed-for-comparison-", + "filename": null, + "syntaktisch": { + "valid": true, + "status": "COMPLETED" + }, + "validationName": "edfd613e-b85d-4ea6-9b97-bb33712b1ba6", + "plans": [ + { + "valid": false, + "externalReferencesResult": [], + "rasterEvaluationResult": [], + "validationResult": { + "semantisch": { + "valid": true, + "rulesMetadata": { + "source": "https:\/\/gitlab.opencode.de\/xleitstelle\/xplanung\/validierungsregeln\/standard\/-\/tree\/v1.1.9", + "version": "1.1.9" + }, + "rules": [ + { + "id": "2.1.2.1", + "title": "Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType" + }, + { + "id": "2.1.3.1", + "title": "Angabe eines Standard CRS" + }, + { + "id": "2.2.1.1", + "title": "Flächenschlussbedingung" + }, + { + "id": "3.1.1.1", + "title": "Relationen auf Text-Abschnitte" + }, + { + "id": "3.1.1.2", + "title": "Relationen auf Begründungs-Abschnitte" + }, + { + "id": "3.1.2.1", + "title": "Relation auf Präsentationsobjekte" + }, + { + "id": "3.1.2.2", + "title": "Relation auf Fachobjekte" + }, + { + "id": "3.1.2.3", + "title": "Relation auf Basis-Rasterplan" + }, + { + "id": "3.1.3.1", + "title": "Relationen auf Text-Abschnitte" + }, + { + "id": "3.1.3.2", + "title": "Relationen auf Begründungs-Abschnitte" + }, + { + "id": "3.1.3.3", + "title": "Rückwärts-Referenzen auf Plan-Bereiche" + }, + { + "id": "3.1.3.4", + "title": "Rückwärts-Referenzen auf Präsentationsobjekte" + }, + { + "id": "3.2.1.1", + "title": "Spezifikation des Textinhalts" + }, + { + "id": "3.2.2.1", + "title": "Spezifikation des Textinhalts" + }, + { + "id": "3.2.3.1", + "title": "Konsistenz der verschiedenen Höhenangaben" + }, + { + "id": "3.2.3.2", + "title": "Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist" + }, + { + "id": "3.2.3.3", + "title": "Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug" + }, + { + "id": "3.2.4.1", + "title": "Verweis auf Dokumente." + }, + { + "id": "3.2.5.1", + "title": "Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel)." + }, + { + "id": "3.3.1.1", + "title": "Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten" + }, + { + "id": "3.3.1.2", + "title": "Konsistenz der Attribute art und index" + }, + { + "id": "3.3.1.3", + "title": "Rückwärts-Referenzen auf Plan-Bereiche" + }, + { + "id": "3.3.2.1", + "title": "Einschränkung des Raumbezugs auf Punktgeometrie." + }, + { + "id": "3.3.3.1", + "title": "Einschränkung des Raumbezugs auf Liniengeometrie." + }, + { + "id": "3.3.4.1", + "title": "Einschränkung des Raumbezugs auf Flächengeometrie." + }, + { + "id": "4.1.1.1", + "title": "Einschränkung der Relation bereich" + }, + { + "id": "4.1.2.1", + "title": "Einschränkung der Relation inhaltBPlan" + }, + { + "id": "4.1.2.2", + "title": "Einschränkung der Relation rasterAenderung" + }, + { + "id": "4.1.2.3", + "title": "Einschränkung der Relation gehoertZuPlan" + }, + { + "id": "4.1.3.1", + "title": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche" + }, + { + "id": "4.1.3.2", + "title": "Einschränkung der Relation wirdAusgeglichenDurchMassnahme" + }, + { + "id": "4.1.3.3", + "title": "Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme" + }, + { + "id": "4.1.3.4", + "title": "Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche" + }, + { + "id": "4.1.3.5", + "title": "Rückwärts-Referenzen auf Plan-Bereiche" + }, + { + "id": "4.1.3.6", + "title": "BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." + }, + { + "id": "4.1.3.7", + "title": "Einschränkung der Relation wirdAusgeglichenDurchABE" + }, + { + "id": "4.1.4.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "4.1.4.2", + "title": "Einschränkung auf Flächengeometrie" + }, + { + "id": "4.1.5.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "4.1.5.2", + "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" + }, + { + "id": "4.1.6.1", + "title": "Ãœberlagerungsobjekte gehören nie zum Flächenschluss." + }, + { + "id": "4.1.7.1", + "title": "Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss" + }, + { + "id": "4.1.8.1", + "title": "Einschränkung auf Liniengeometrie" + }, + { + "id": "4.1.9.1", + "title": "Einschränkung auf Punktgeometrie" + }, + { + "id": "4.2.1", + "title": "Konsistenz der Angaben zur GFZ" + }, + { + "id": "4.2.2", + "title": "Konsistenz der Angaben zur GF" + }, + { + "id": "4.2.3", + "title": "Konsistenz der Angaben zur GFZ und GF" + }, + { + "id": "4.2.4", + "title": "Konsistenz der Angaben zur BMZ" + }, + { + "id": "4.2.5", + "title": "Konsistenz der Angaben zur BM" + }, + { + "id": "4.2.6", + "title": "Konsistenz der Angaben zur BMZ und BM" + }, + { + "id": "4.2.7", + "title": "Konsistenz der Angaben zur GRZ" + }, + { + "id": "4.2.8", + "title": "Konsistenz der Angaben zur GR" + }, + { + "id": "4.2.9", + "title": "Konsistenz der Angaben zur GRZ und GR" + }, + { + "id": "4.2.10", + "title": "Konsistenz der Angaben zu Z" + }, + { + "id": "4.2.11", + "title": "Konsistenz der Angaben zu ZU" + }, + { + "id": "4.3.1", + "title": "Konsistenz der Angaben zur Dachneigung" + }, + { + "id": "4.3.2", + "title": "Konsistenz der Attribute dachform und detaillierteDachform" + }, + { + "id": "4.5.1.1", + "title": "Relation abweichungText" + }, + { + "id": "4.5.1.2", + "title": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung" + }, + { + "id": "4.5.1.3", + "title": "Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung" + }, + { + "id": "4.5.1.4", + "title": "Konsistenz der Attribute bauweise und abweichendeBauweise" + }, + { + "id": "4.5.1.5", + "title": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung" + }, + { + "id": "4.5.2.1", + "title": "Relation baugrenze" + }, + { + "id": "4.5.2.2", + "title": "Relation baulinie" + }, + { + "id": "4.5.10.1", + "title": "Konsistenz der Attribute typ und sonstTyp" + }, + { + "id": "4.5.13.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.5.13.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.5.13.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.5.14.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.5.14.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.5.14.3", + "title": "Einschränkung der Relation eigentümer" + }, + { + "id": "4.5.14.4", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.5.15.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.6.3.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.7.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.7.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "4.7.1.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.7.1.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "4.7.1.5", + "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)" + }, + { + "id": "4.7.1.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "4.7.2.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.7.2.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.7.2.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.8.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.8.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.8.1.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.8.2.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.8.2.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.8.2.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.8.2.4", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.8.3.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.8.3.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "4.8.3.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.8.3.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "4.8.3.5", + "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi" + }, + { + "id": "4.8.3.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "4.9.2.1", + "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" + }, + { + "id": "4.9.2.2", + "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" + }, + { + "id": "4.9.2.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" + }, + { + "id": "4.9.3.1", + "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" + }, + { + "id": "4.9.3.2", + "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" + }, + { + "id": "4.9.3.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" + }, + { + "id": "4.9.5.1", + "title": "Konsistenz der Attribute massnahme und weitereMassnahme1" + }, + { + "id": "4.9.5.2", + "title": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1" + }, + { + "id": "4.9.5.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" + }, + { + "id": "4.9.5.4", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.9.6.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.10.2.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.11.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.11.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "4.11.1.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "4.11.1.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "4.11.1.5", + "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi" + }, + { + "id": "4.11.1.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "4.11.1.7", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.12.1.1", + "title": "Einschränkung der Relation begrenzungslinie" + }, + { + "id": "4.12.2.1", + "title": "Einschränkung der Relation begrenzungslinie" + }, + { + "id": "4.12.2.2", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "4.12.3.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.13.1.1", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "4.13.2.1", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "4.14.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.14.2.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "4.14.3.1", + "title": "Konsistenz der Attribute startWinkel und endWinkel" + }, + { + "id": "4.14.3.2", + "title": "Kein flächenhafter Raumbezug" + }, + { + "id": "4.14.4.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "4.14.6.1", + "title": "Notwendige Spezifikation einer Textlichen Darstellung" + }, + { + "id": "4.14.7.1", + "title": "Verwendung der Relation hoehenangabe" + }, + { + "id": "4.14.7.2", + "title": "Kein flächenhafter Raumbezug" + }, + { + "id": "5.1.1.1", + "title": "Einschränkung der Relation bereich" + }, + { + "id": "5.1.1.2", + "title": "Konsistenz der Attribute planArt und sonstPlanArt" + }, + { + "id": "5.1.2.1", + "title": "Einschränkung der Relation inhaltFPlan" + }, + { + "id": "5.1.2.2", + "title": "Einschränkung der Relation rasterAenderung" + }, + { + "id": "5.1.2.3", + "title": "Einschränkung der Relation gehoertZuPlan" + }, + { + "id": "5.1.3.1", + "title": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche" + }, + { + "id": "5.1.3.2", + "title": "Einschränkung der Relation wirdAusgeglichenDurchSPE" + }, + { + "id": "5.1.3.3", + "title": "Rückwärts-Referenzen auf Plan-Bereiche" + }, + { + "id": "5.1.3.4", + "title": "FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." + }, + { + "id": "5.1.4.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "5.1.4.2", + "title": "Einschränkung auf Flächengeometrie" + }, + { + "id": "5.1.5.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "5.1.5.2", + "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" + }, + { + "id": "5.1.6.1", + "title": "Ãœberlagerungsobjekte gehören nie zum Flächenschluss" + }, + { + "id": "5.1.7.1", + "title": "Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss" + }, + { + "id": "5.1.8.1", + "title": "Einschränkung auf Liniengeometrie" + }, + { + "id": "5.1.9.1", + "title": "Einschränkung auf Punktgeometrie" + }, + { + "id": "5.3.1.1", + "title": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung" + }, + { + "id": "5.3.1.2", + "title": "Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung" + }, + { + "id": "5.3.1.3", + "title": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung" + }, + { + "id": "5.3.1.4", + "title": "Konsistenz der Angaben zur GFZ" + }, + { + "id": "5.4.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.4.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "5.4.1.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "5.4.1.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "5.4.1.5", + "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)" + }, + { + "id": "5.4.1.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "5.4.1.7", + "title": "Einschränkung des Raumbezugs" + }, + { + "id": "5.4.1.8", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.4.2.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.4.2.2", + "title": "Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung" + }, + { + "id": "5.4.2.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "5.4.2.4", + "title": "Einschränkung des Raumbezugs" + }, + { + "id": "5.4.2.5", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.5.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.5.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "5.5.1.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "5.5.2.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.5.2.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "5.5.2.3", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "5.5.3.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.5.3.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "5.5.3.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "5.5.3.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "5.5.3.5", + "title": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)" + }, + { + "id": "5.5.3.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "5.5.3.7", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.6.1.1", + "title": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2" + }, + { + "id": "5.6.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" + }, + { + "id": "5.6.2.1", + "title": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2" + }, + { + "id": "5.6.2.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen" + }, + { + "id": "5.7.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.7.1.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "5.7.1.3", + "title": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen" + }, + { + "id": "5.7.1.4", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "5.7.1.5", + "title": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)" + }, + { + "id": "5.7.1.6", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "5.7.1.7", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.8.1.1", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "5.8.1.2", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung" + }, + { + "id": "5.8.1.3", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.9.1.1", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "5.9.1.2", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.9.2.1", + "title": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung" + }, + { + "id": "5.9.2.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.10.1.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.10.2.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.10.2.2", + "title": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen" + }, + { + "id": "5.10.2.3", + "title": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung" + }, + { + "id": "5.10.2.4", + "title": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)" + }, + { + "id": "5.10.3.1", + "title": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen" + }, + { + "id": "5.10.4.1", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "5.10.6.1", + "title": "Notwendige Spezifikation einer Textlichen Darstellung" + }, + { + "id": "6.1.1.1", + "title": "Einschränkung der Relation bereich" + }, + { + "id": "6.1.2.1", + "title": "Einschränkung der Relation inhaltSoPlan" + }, + { + "id": "6.1.2.2", + "title": "Einschränkung der Relation rasterAenderung" + }, + { + "id": "6.1.2.3", + "title": "Einschränkung der Relation gehoertZuPlan" + }, + { + "id": "6.1.3.1", + "title": "Rückwärts-Referenzen auf Plan-Bereiche" + }, + { + "id": "6.1.3.2", + "title": "SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Ãœbernahme in den Plan integriert werden." + }, + { + "id": "6.1.4.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "6.1.4.2", + "title": "Einschränkung auf Flächengeometrie" + }, + { + "id": "6.1.5.1", + "title": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss" + }, + { + "id": "6.1.5.2", + "title": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug" + }, + { + "id": "6.1.6.1", + "title": "Einschränkung auf Liniengeometrie" + }, + { + "id": "6.1.7.1", + "title": "Einschränkung auf Punktgeometrie" + }, + { + "id": "6.2.1.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.1.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.2.2.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.2.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.2.3.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.4.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.5.1", + "title": "Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung" + }, + { + "id": "6.2.5.2", + "title": "Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.5.3", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.2.6.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.7.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.2.7.2", + "title": "Flächenschlussobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.2.8.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.3.1.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.3.1.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.3.2.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.3.2.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.3.3.1", + "title": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung" + }, + { + "id": "6.3.3.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.4.1.1", + "title": "Konsistenz der Attribute gebietsArt und sonstGebietsArt" + }, + { + "id": "6.4.1.2", + "title": "Ãœberlagerungsobjekt bei flächenhaftem Raumbezug" + }, + { + "id": "6.5.1.1", + "title": "Konsistenz von typ und sonstTyp" + } + ], + "status": "COMPLETED" + }, + "geometrisch": { + "valid": false, + "rules": [ + { + "id": "2.2.1.1", + "title": "Flächenschlussbedingung" + }, + { + "id": "2.2.3.1", + "title": "Raumbezogene Objekte im Innern des Geltungsbereichs" + }, + { + "findings": [ + { + "markerGeom": { + "bbox": null, + "coordinates": [ + [ + 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 + ] ], - "status": "COMPLETED" + "type": "LineString" + }, + "level": "ERROR", + "gmlIds": [ + "GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3" + ], + "message": "äußerer Ring verwendet falsche Laufrichtung (CW)." } - }, - "bbox": { - "minY": 52.33234200586314, - "minX": 7.373668092967802, - "crs": "EPSG:4326", - "maxY": 52.33376312995529, - "maxX": 7.377600099759094 - }, - "name": "Nr. 108 Holstener Weg", - "type": "BP_Plan", - "version": "XPLAN_41", - "status": "COMPLETED" + ], + "id": "2.2.2.1", + "title": "Verwendung geometrisch korrekter Flächen" + } + ], + "status": "COMPLETED" + } + }, + "bbox": { + "minY": 52.33234200586314, + "minX": 7.373668092967802, + "crs": "EPSG:4326", + "maxY": 52.33376312995529, + "maxX": 7.377600099759094 + }, + "name": "Nr. 108 Holstener Weg", + "type": "BP_Plan", + "version": "XPLAN_41", + "status": "COMPLETED" + } + ], + "geomfindings": { + "features": [ + { + "bbox": null, + "geometry": { + "bbox": null, + "coordinates": [ + [ + 7.373668092967802, + 52.33370974642528 + ], + [ + 7.373740630960223, + 52.3337072834449 + ], + [ + 7.374060422654426, + 52.33369646339027 + ], + [ + 7.374268529524706, + 52.33368936534038 + ], + [ + 7.374372603139325, + 52.33368593801491 + ], + [ + 7.37484275647524, + 52.33367045484784 + ], + [ + 7.375032114098382, + 52.33366432032051 + ], + [ + 7.375525875758316, + 52.333647500474676 + ], + [ + 7.375622882227448, + 52.333644194970056 + ], + [ + 7.375653363897409, + 52.33364315735862 + ], + [ + 7.375696066826445, + 52.333641703716076 + ], + [ + 7.37585262040787, + 52.33363636657995 + ], + [ + 7.375894131731375, + 52.33363466252641 + ], + [ + 7.3761971480516255, + 52.33362218540471 + ], + [ + 7.376555065348309, + 52.33358825340854 + ], + [ + 7.376788479046236, + 52.33355579243725 + ], + [ + 7.3772159550394045, + 52.33348567098335 + ], + [ + 7.377264313592806, + 52.333477739592674 + ], + [ + 7.377338034845487, + 52.333460176188595 + ], + [ + 7.377423230495644, + 52.33343989312186 + ], + [ + 7.377447468513561, + 52.33343412093081 + ], + [ + 7.3774533614179765, + 52.333432718302774 + ], + [ + 7.377513948932358, + 52.333418292192746 + ], + [ + 7.37754024479394, + 52.33341203574116 + ], + [ + 7.3775630909820915, + 52.333406595072084 + ], + [ + 7.37732986037478, + 52.33330969543118 + ], + [ + 7.377110068687087, + 52.33321837775881 + ], + [ + 7.377032473686757, + 52.33318621054016 + ], + [ + 7.377095712470633, + 52.33287926024749 + ], + [ + 7.377112282053984, + 52.332798700540955 + ], + [ + 7.3771266853664, + 52.332728801794815 + ], + [ + 7.377159304723172, + 52.332570246754656 + ], + [ + 7.376854404840756, + 52.332547995677096 + ], + [ + 7.376778177752862, + 52.33254243262653 + ], + [ + 7.376397609674328, + 52.332513797742436 + ], + [ + 7.376193941152073, + 52.33249965999457 + ], + [ + 7.376179293458316, + 52.3324992608228 + ], + [ + 7.3759795292286245, + 52.33249673926654 + ], + [ + 7.375920848842603, + 52.332495986417904 + ], + [ + 7.37570612100142, + 52.33249327664225 + ], + [ + 7.375619976833131, + 52.33249220892396 + ], + [ + 7.37555681531653, + 52.33248411185837 + ], + [ + 7.375424663045128, + 52.332467170322104 + ], + [ + 7.375102830883236, + 52.33242579807323 + ], + [ + 7.375058042725434, + 52.332420029900085 + ], + [ + 7.3749468907495155, + 52.33240570606995 + ], + [ + 7.374921794186274, + 52.33240247457392 + ], + [ + 7.3747340037440186, + 52.332378419482254 + ], + [ + 7.374617501709999, + 52.33236350921986 + ], + [ + 7.374602909106646, + 52.33236160905276 + ], + [ + 7.374538107172301, + 52.33235329046701 + ], + [ + 7.373668092967802, + 52.33370974642528 + ] + ], + "type": "LineString" + }, + "id": null, + "type": "Feature", + "properties": { + "level": "ERROR", + "gmlIds": "GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3", + "planName": "Nr. 108 Holstener Weg", + "id": "2.2.2.1", + "title": "Verwendung geometrisch korrekter Flächen", + "message": "äußerer Ring verwendet falsche Laufrichtung (CW)." } + } ], - "status": "COMPLETED" + "bbox": null, + "type": "FeatureCollection" + }, + "status": "COMPLETED" } \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.json b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.json index 1a1fa0fb1c..6749a7ef50 100644 --- a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.json +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.json @@ -997,6 +997,7 @@ "findings": [ { "markerGeom": { + "bbox": null, "coordinates": [ [ 389187.29, @@ -1236,5 +1237,238 @@ "status": "COMPLETED" } ], + "geomfindings": { + "features": [ + { + "bbox": null, + "geometry": { + "bbox": null, + "coordinates": [ + [ + 7.373668092967802, + 52.33370974642528 + ], + [ + 7.373740630960223, + 52.3337072834449 + ], + [ + 7.374060422654426, + 52.33369646339027 + ], + [ + 7.374268529524706, + 52.33368936534038 + ], + [ + 7.374372603139325, + 52.33368593801491 + ], + [ + 7.37484275647524, + 52.33367045484784 + ], + [ + 7.375032114098382, + 52.33366432032051 + ], + [ + 7.375525875758316, + 52.333647500474676 + ], + [ + 7.375622882227448, + 52.333644194970056 + ], + [ + 7.375653363897409, + 52.33364315735862 + ], + [ + 7.375696066826445, + 52.333641703716076 + ], + [ + 7.37585262040787, + 52.33363636657995 + ], + [ + 7.375894131731375, + 52.33363466252641 + ], + [ + 7.3761971480516255, + 52.33362218540471 + ], + [ + 7.376555065348309, + 52.33358825340854 + ], + [ + 7.376788479046236, + 52.33355579243725 + ], + [ + 7.3772159550394045, + 52.33348567098335 + ], + [ + 7.377264313592806, + 52.333477739592674 + ], + [ + 7.377338034845487, + 52.333460176188595 + ], + [ + 7.377423230495644, + 52.33343989312186 + ], + [ + 7.377447468513561, + 52.33343412093081 + ], + [ + 7.3774533614179765, + 52.333432718302774 + ], + [ + 7.377513948932358, + 52.333418292192746 + ], + [ + 7.37754024479394, + 52.33341203574116 + ], + [ + 7.3775630909820915, + 52.333406595072084 + ], + [ + 7.37732986037478, + 52.33330969543118 + ], + [ + 7.377110068687087, + 52.33321837775881 + ], + [ + 7.377032473686757, + 52.33318621054016 + ], + [ + 7.377095712470633, + 52.33287926024749 + ], + [ + 7.377112282053984, + 52.332798700540955 + ], + [ + 7.3771266853664, + 52.332728801794815 + ], + [ + 7.377159304723172, + 52.332570246754656 + ], + [ + 7.376854404840756, + 52.332547995677096 + ], + [ + 7.376778177752862, + 52.33254243262653 + ], + [ + 7.376397609674328, + 52.332513797742436 + ], + [ + 7.376193941152073, + 52.33249965999457 + ], + [ + 7.376179293458316, + 52.3324992608228 + ], + [ + 7.3759795292286245, + 52.33249673926654 + ], + [ + 7.375920848842603, + 52.332495986417904 + ], + [ + 7.37570612100142, + 52.33249327664225 + ], + [ + 7.375619976833131, + 52.33249220892396 + ], + [ + 7.37555681531653, + 52.33248411185837 + ], + [ + 7.375424663045128, + 52.332467170322104 + ], + [ + 7.375102830883236, + 52.33242579807323 + ], + [ + 7.375058042725434, + 52.332420029900085 + ], + [ + 7.3749468907495155, + 52.33240570606995 + ], + [ + 7.374921794186274, + 52.33240247457392 + ], + [ + 7.3747340037440186, + 52.332378419482254 + ], + [ + 7.374617501709999, + 52.33236350921986 + ], + [ + 7.374602909106646, + 52.33236160905276 + ], + [ + 7.374538107172301, + 52.33235329046701 + ], + [ + 7.373668092967802, + 52.33370974642528 + ] + ], + "type": "LineString" + }, + "id": null, + "type": "Feature", + "properties": { + "level": "ERROR", + "gmlIds": "GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3", + "planName": "Nr. 108 Holstener Weg", + "id": "2.2.2.1", + "title": "Verwendung geometrisch korrekter Flächen", + "message": "äußerer Ring verwendet falsche Laufrichtung (CW)." + } + } + ], + "bbox": null, + "type": "FeatureCollection" + }, "status": "COMPLETED" } \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report3.expected.json b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report3.expected.json index 9c2f536daf..8276a83c50 100644 --- a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report3.expected.json +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report3.expected.json @@ -1011,6 +1011,7 @@ "findings": [ { "markerGeom": { + "bbox": null, "coordinates": [ [ 389187.29, @@ -1250,5 +1251,238 @@ "status": "COMPLETED" } ], + "geomfindings": { + "features": [ + { + "bbox": null, + "geometry": { + "bbox": null, + "coordinates": [ + [ + 7.373668092967802, + 52.33370974642528 + ], + [ + 7.373740630960223, + 52.3337072834449 + ], + [ + 7.374060422654426, + 52.33369646339027 + ], + [ + 7.374268529524706, + 52.33368936534038 + ], + [ + 7.374372603139325, + 52.33368593801491 + ], + [ + 7.37484275647524, + 52.33367045484784 + ], + [ + 7.375032114098382, + 52.33366432032051 + ], + [ + 7.375525875758316, + 52.333647500474676 + ], + [ + 7.375622882227448, + 52.333644194970056 + ], + [ + 7.375653363897409, + 52.33364315735862 + ], + [ + 7.375696066826445, + 52.333641703716076 + ], + [ + 7.37585262040787, + 52.33363636657995 + ], + [ + 7.375894131731375, + 52.33363466252641 + ], + [ + 7.3761971480516255, + 52.33362218540471 + ], + [ + 7.376555065348309, + 52.33358825340854 + ], + [ + 7.376788479046236, + 52.33355579243725 + ], + [ + 7.3772159550394045, + 52.33348567098335 + ], + [ + 7.377264313592806, + 52.333477739592674 + ], + [ + 7.377338034845487, + 52.333460176188595 + ], + [ + 7.377423230495644, + 52.33343989312186 + ], + [ + 7.377447468513561, + 52.33343412093081 + ], + [ + 7.3774533614179765, + 52.333432718302774 + ], + [ + 7.377513948932358, + 52.333418292192746 + ], + [ + 7.37754024479394, + 52.33341203574116 + ], + [ + 7.3775630909820915, + 52.333406595072084 + ], + [ + 7.37732986037478, + 52.33330969543118 + ], + [ + 7.377110068687087, + 52.33321837775881 + ], + [ + 7.377032473686757, + 52.33318621054016 + ], + [ + 7.377095712470633, + 52.33287926024749 + ], + [ + 7.377112282053984, + 52.332798700540955 + ], + [ + 7.3771266853664, + 52.332728801794815 + ], + [ + 7.377159304723172, + 52.332570246754656 + ], + [ + 7.376854404840756, + 52.332547995677096 + ], + [ + 7.376778177752862, + 52.33254243262653 + ], + [ + 7.376397609674328, + 52.332513797742436 + ], + [ + 7.376193941152073, + 52.33249965999457 + ], + [ + 7.376179293458316, + 52.3324992608228 + ], + [ + 7.3759795292286245, + 52.33249673926654 + ], + [ + 7.375920848842603, + 52.332495986417904 + ], + [ + 7.37570612100142, + 52.33249327664225 + ], + [ + 7.375619976833131, + 52.33249220892396 + ], + [ + 7.37555681531653, + 52.33248411185837 + ], + [ + 7.375424663045128, + 52.332467170322104 + ], + [ + 7.375102830883236, + 52.33242579807323 + ], + [ + 7.375058042725434, + 52.332420029900085 + ], + [ + 7.3749468907495155, + 52.33240570606995 + ], + [ + 7.374921794186274, + 52.33240247457392 + ], + [ + 7.3747340037440186, + 52.332378419482254 + ], + [ + 7.374617501709999, + 52.33236350921986 + ], + [ + 7.374602909106646, + 52.33236160905276 + ], + [ + 7.374538107172301, + 52.33235329046701 + ], + [ + 7.373668092967802, + 52.33370974642528 + ] + ], + "type": "LineString" + }, + "id": null, + "type": "Feature", + "properties": { + "level": "ERROR", + "gmlIds": "GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3", + "planName": "Nr. 108 Holstener Weg", + "id": "2.2.2.1", + "title": "Verwendung geometrisch korrekter Flächen", + "message": "äußerer Ring verwendet falsche Laufrichtung (CW)." + } + } + ], + "bbox": null, + "type": "FeatureCollection" + }, "status": "COMPLETED" } \ No newline at end of file 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 index b9d1ea3844..072f5a4275 100644 --- 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 @@ -48,7 +48,7 @@ public abstract class ValidationExecutionStorage { public enum ReportType { - JSON(".json"), PDF(".pdf"), ZIP(".zip"); + JSON(".json"), PDF(".pdf"), GEOJSON(".geojson"), ZIP(".zip"); private final String fileExtension; diff --git a/xplan-validator/xplan-validator-web/src/main/java/de/latlon/xplan/validator/web/server/service/ValidatorReportProvider.java b/xplan-validator/xplan-validator-web/src/main/java/de/latlon/xplan/validator/web/server/service/ValidatorReportProvider.java index 27159e21ab..1405f42421 100644 --- a/xplan-validator/xplan-validator-web/src/main/java/de/latlon/xplan/validator/web/server/service/ValidatorReportProvider.java +++ b/xplan-validator/xplan-validator-web/src/main/java/de/latlon/xplan/validator/web/server/service/ValidatorReportProvider.java @@ -22,7 +22,7 @@ package de.latlon.xplan.validator.web.server.service; import de.latlon.xplanbox.core.gwt.commons.server.service.ReportProvider; import de.latlon.xplan.validator.report.ReportWriter; -import de.latlon.xplan.validator.web.shared.ArtifactType; +import de.latlon.xplan.validator.web.shared.ReportFormatType; import org.springframework.beans.factory.annotation.Autowired; import jakarta.servlet.http.HttpServletResponse; @@ -60,7 +60,7 @@ public class ValidatorReportProvider implements ReportProvider { @Override public void writeZipReport(HttpServletResponse response, String planUuid, String validationName, - List<ArtifactType> artifacts) throws IOException { + List<ReportFormatType> artifacts) throws IOException { Path planDirectory = planArchiveManager.createReportDirectory(planUuid); reportWriter.writeZipWithArtifacts(response.getOutputStream(), validationName, artifacts, planDirectory); } -- GitLab