diff --git a/src/main/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperation.java b/src/main/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperation.java index 3e35dbd5..c623fc57 100644 --- a/src/main/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperation.java +++ b/src/main/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperation.java @@ -5,6 +5,7 @@ import static org.opengis.cite.wfs30.SuiteAttribute.API_MODEL; import static org.opengis.cite.wfs30.WFS3.PATH.COLLECTIONS; import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPoints; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForCollectionMetadata; import static org.opengis.cite.wfs30.util.JsonUtils.findLinkByRel; import static org.opengis.cite.wfs30.util.JsonUtils.findLinksWithSupportedMediaTypeByRel; import static org.opengis.cite.wfs30.util.JsonUtils.findLinksWithoutRelOrType; @@ -21,8 +22,8 @@ import org.opengis.cite.wfs30.CommonFixture; import org.opengis.cite.wfs30.SuiteAttribute; -import org.opengis.cite.wfs30.openapi3.OpenApiUtils; import org.opengis.cite.wfs30.openapi3.TestPoint; +import org.opengis.cite.wfs30.openapi3.UriBuilder; import org.testng.ITestContext; import org.testng.SkipException; import org.testng.annotations.AfterClass; @@ -45,8 +46,6 @@ public class FeatureCollectionsMetadataOperation extends CommonFixture { private final Map>> testPointAndCollections = new HashMap<>(); - private final List collectionNamesFromLandingPage = new ArrayList<>(); - private OpenApi3 apiModel; private Object[][] testPointsData; @@ -75,19 +74,6 @@ public Object[][] collections( ITestContext testContext ) { return objects; } - @BeforeClass - public void parseRequiredMetadata( ITestContext testContext ) { - Response request = init().baseUri( rootUri.toString() ).accept( JSON ).when().request( GET, "/" ); - JsonPath jsonPath = request.jsonPath(); - - List collections = jsonPath.getList( "collections" ); - for ( Object collectionObject : collections ) { - Map collection = (Map) collectionObject; - String collectionName = (String) collection.get( "name" ); - this.collectionNamesFromLandingPage.add( collectionName ); - } - } - @BeforeClass public void openApiDocument( ITestContext testContext ) { this.apiModel = (OpenApi3) testContext.getSuite().getAttribute( API_MODEL.getName() ); @@ -122,7 +108,7 @@ public void storeCollectionsInTestContext( ITestContext testContext ) { */ @Test(description = "Implements A.4.4.4. Validate the Feature Collections Metadata Operation (Requirement 9, 10)", groups = "collections", dataProvider = "collectionsUris", dependsOnGroups = "apidefinition") public void validateFeatureCollectionsMetadataOperation( TestPoint testPoint ) { - String testPointUri = testPoint.createUri(); + String testPointUri = new UriBuilder( testPoint ).buildUrl(); Response response = init().baseUri( testPointUri ).accept( JSON ).when().request( GET ); response.then().statusCode( 200 ); this.testPointAndResponses.put( testPoint, response ); @@ -201,10 +187,8 @@ public void validateFeatureCollectionsMetadataOperationResponse_Collections( Tes JsonPath jsonPath = response.jsonPath(); List collections = jsonPath.getList( "collections" ); - List missingCollectionNames = findMissingCollectionNames( collections ); - assertTrue( missingCollectionNames.isEmpty(), - "Feature Collection Metadata document must include a collections property for each collection in the dataset. Missing collection properties " - + missingCollectionNames ); + // Test method cannot be verified as the provided collections are not known. + this.testPointAndCollections.put( testPoint, createCollectionsMap( collections ) ); } @@ -234,8 +218,8 @@ public void validateFeatureCollectionsMetadataOperationResponse_Collections( Tes @Test(description = "Implements A.4.4.6. Validate a Collections Metadata document (Requirement 13)", groups = "collections", dataProvider = "collections", dependsOnMethods = "validateFeatureCollectionsMetadataOperationResponse_Collections") public void validateCollectionsMetadataDocument_Links( TestPoint testPoint, Map collection ) { String collectionName = (String) collection.get( "name" ); - List testPointsForNamedCollection = OpenApiUtils.retrieveTestPoints( apiModel, COLLECTIONS, - collectionName ); + List testPointsForNamedCollection = retrieveTestPointsForCollectionMetadata( apiModel, + collectionName ); if ( testPointsForNamedCollection.isEmpty() ) throw new SkipException( "Could not find collection with name " + collectionName + " in the OpenAPI document" ); @@ -291,13 +275,15 @@ public void validateCollectionsMetadataDocument_Extent( TestPoint testPoint, Map public void validateTheFeatureCollectionMetadataOperationAndResponse( TestPoint testPoint, Map collection ) { String collectionName = (String) collection.get( "name" ); - List testPointsForNamedCollection = OpenApiUtils.retrieveTestPoints( apiModel, COLLECTIONS, - collectionName ); + List testPointsForNamedCollection = retrieveTestPointsForCollectionMetadata( apiModel, + collectionName ); if ( testPointsForNamedCollection.isEmpty() ) throw new SkipException( "Could not find collection with name " + collectionName + " in the OpenAPI document" ); - Response response = validateTheFeatureCollectionMetadataOperationAndResponse( testPointsForNamedCollection.get( 0 ) ); + TestPoint testPointCollectionMetadata = testPointsForNamedCollection.get( 0 ); + Response response = validateTheFeatureCollectionMetadataOperationAndResponse( testPointCollectionMetadata, + collectionName ); validateFeatureCollectionMetadataOperationResponse( response, collection ); } @@ -321,12 +307,14 @@ public void validateTheFeatureCollectionMetadataOperationAndResponse( TestPoint * Go to test A.4.4.8 * * d) References: Requirement 15 - * + * * @param testPoint * to test, never null + * @param collectionName */ - private Response validateTheFeatureCollectionMetadataOperationAndResponse( TestPoint testPoint ) { - String testPointUri = testPoint.createUri(); + private Response validateTheFeatureCollectionMetadataOperationAndResponse( TestPoint testPoint, + String collectionName ) { + String testPointUri = new UriBuilder( testPoint ).collectionName( collectionName ).buildUrl(); Response response = init().baseUri( testPointUri ).accept( JSON ).when().request( GET ); response.then().statusCode( 200 ); return response; @@ -356,26 +344,6 @@ private void validateFeatureCollectionMetadataOperationResponse( Response respon assertEquals( collection, jsonPath.get() ); } - private List findMissingCollectionNames( List collections ) { - List missingCollectionNames = new ArrayList<>(); - for ( String collectionNameFromLandingPage : this.collectionNamesFromLandingPage ) { - Map collection = findCollectionByName( collectionNameFromLandingPage, collections ); - if ( collection == null ) - missingCollectionNames.add( collectionNameFromLandingPage ); - } - return missingCollectionNames; - } - - private Map findCollectionByName( String collectionNameFromLandingPage, List collections ) { - for ( Object collectionObject : collections ) { - Map collection = (Map) collectionObject; - Object collectionName = collection.get( "name" ); - if ( collectionNameFromLandingPage.equals( collectionName ) ) - return collection; - } - return null; - } - private List> createCollectionsMap( List collections ) { List> collectionsMap = new ArrayList<>(); for ( Object collection : collections ) diff --git a/src/main/java/org/opengis/cite/wfs30/collections/GetFeatureOperation.java b/src/main/java/org/opengis/cite/wfs30/collections/GetFeatureOperation.java index 36b6478a..30488b3c 100644 --- a/src/main/java/org/opengis/cite/wfs30/collections/GetFeatureOperation.java +++ b/src/main/java/org/opengis/cite/wfs30/collections/GetFeatureOperation.java @@ -3,8 +3,7 @@ import static io.restassured.http.Method.GET; import static org.opengis.cite.wfs30.SuiteAttribute.API_MODEL; import static org.opengis.cite.wfs30.WFS3.GEOJSON_MIME_TYPE; -import static org.opengis.cite.wfs30.WFS3.PATH.COLLECTIONS; -import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPoints; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForFeature; import static org.opengis.cite.wfs30.util.JsonUtils.findLinkByRel; import static org.opengis.cite.wfs30.util.JsonUtils.findLinksWithSupportedMediaTypeByRel; import static org.opengis.cite.wfs30.util.JsonUtils.findLinksWithoutRelOrType; @@ -45,15 +44,6 @@ public class GetFeatureOperation extends CommonFixture { private final Map collectionNameAndResponse = new HashMap<>(); - @DataProvider(name = "collectionItemUris") - public Iterator collectionItemUris( ITestContext testContext ) { - List collectionsData = new ArrayList<>(); - for ( Map collection : collections ) { - collectionsData.add( new Object[] { collection } ); - } - return collectionsData.iterator(); - } - @DataProvider(name = "collectionFeatureId") public Iterator collectionFeatureId( ITestContext testContext ) { Map collectionNameToFeatureId = (Map) testContext.getSuite().getAttribute( SuiteAttribute.FEATUREIDS.getName() ); @@ -154,15 +144,16 @@ public void getFeatureOperation( Map collection, String featureI * @param collection * the collection under test, never null */ - @Test(description = "Implements A.4.4.15. Validate the Get Feature Operation Response (Requirement 32)", dataProvider = "collectionItemUris", dependsOnMethods = "getFeatureOperation") - public void validateTheGetFeatureOperationResponse( Map collection ) { + @Test(description = "Implements A.4.4.15. Validate the Get Feature Operation Response (Requirement 32)", dataProvider = "collectionFeatureId", dependsOnMethods = "getFeatureOperation") + public void validateTheGetFeatureOperationResponse( Map collection, String featureId ) { String collectionName = (String) collection.get( "name" ); Response response = collectionNameAndResponse.get( collectionName ); if ( response == null ) throw new SkipException( "Could not find a response for collection with name " + collectionName ); - List testPointsForNamedCollection = retrieveTestPoints( apiModel, COLLECTIONS, - collectionName + "\\/items\\/\\{.*\\}" ); + List testPointsForNamedCollection = retrieveTestPointsForFeature( apiModel, collectionName, + featureId ); + if ( testPointsForNamedCollection.isEmpty() ) throw new SkipException( "Could not find collection with name " + collectionName + " in the OpenAPI document" ); diff --git a/src/main/java/org/opengis/cite/wfs30/collections/GetFeaturesOperation.java b/src/main/java/org/opengis/cite/wfs30/collections/GetFeaturesOperation.java index 1eea7307..682477de 100644 --- a/src/main/java/org/opengis/cite/wfs30/collections/GetFeaturesOperation.java +++ b/src/main/java/org/opengis/cite/wfs30/collections/GetFeaturesOperation.java @@ -4,6 +4,7 @@ import static org.opengis.cite.wfs30.SuiteAttribute.API_MODEL; import static org.opengis.cite.wfs30.WFS3.GEOJSON_MIME_TYPE; import static org.opengis.cite.wfs30.WFS3.PATH.COLLECTIONS; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForCollection; import static org.opengis.cite.wfs30.util.JsonUtils.collectNumberOfAllReturnedFeatures; import static org.opengis.cite.wfs30.util.JsonUtils.findLinkByRel; import static org.opengis.cite.wfs30.util.JsonUtils.findLinksWithSupportedMediaTypeByRel; @@ -35,7 +36,6 @@ import org.opengis.cite.wfs30.CommonFixture; import org.opengis.cite.wfs30.SuiteAttribute; -import org.opengis.cite.wfs30.openapi3.OpenApiUtils; import org.opengis.cite.wfs30.openapi3.TestPoint; import org.opengis.cite.wfs30.util.BBox; import org.opengis.cite.wfs30.util.TemporalExtent; @@ -217,8 +217,7 @@ public void validateTheGetFeaturesOperationResponse_Links( Map c if ( response == null ) throw new SkipException( "Could not find a response for collection with name " + collectionName ); - List testPointsForNamedCollection = OpenApiUtils.retrieveTestPoints( apiModel, COLLECTIONS, - collectionName + "/items" ); + List testPointsForNamedCollection = retrieveTestPointsForCollection( apiModel, collectionName ); if ( testPointsForNamedCollection.isEmpty() ) throw new SkipException( "Could not find collection with name " + collectionName + " in the OpenAPI document" ); diff --git a/src/main/java/org/opengis/cite/wfs30/conformance/ConformanceOperation.java b/src/main/java/org/opengis/cite/wfs30/conformance/ConformanceOperation.java index 847cc5b7..322d2c1d 100644 --- a/src/main/java/org/opengis/cite/wfs30/conformance/ConformanceOperation.java +++ b/src/main/java/org/opengis/cite/wfs30/conformance/ConformanceOperation.java @@ -12,6 +12,7 @@ import org.opengis.cite.wfs30.CommonFixture; import org.opengis.cite.wfs30.openapi3.TestPoint; +import org.opengis.cite.wfs30.openapi3.UriBuilder; import org.testng.ITestContext; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -63,7 +64,7 @@ public void validateConformanceOperationAndResponse( TestPoint testPoint ) { * d) References: Requirement 5 */ private Response validateConformanceOperation( TestPoint testPoint ) { - String testPointUri = testPoint.createUri(); + String testPointUri = new UriBuilder( testPoint ).buildUrl(); return init().baseUri( testPointUri ).accept( JSON ).when().request( GET ); } diff --git a/src/main/java/org/opengis/cite/wfs30/openapi3/OpenApiUtils.java b/src/main/java/org/opengis/cite/wfs30/openapi3/OpenApiUtils.java index 6ed753ec..90426255 100644 --- a/src/main/java/org/opengis/cite/wfs30/openapi3/OpenApiUtils.java +++ b/src/main/java/org/opengis/cite/wfs30/openapi3/OpenApiUtils.java @@ -1,11 +1,14 @@ package org.opengis.cite.wfs30.openapi3; +import static org.opengis.cite.wfs30.WFS3.PATH.COLLECTIONS; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; import org.opengis.cite.wfs30.WFS3.PATH; @@ -18,6 +21,7 @@ import com.reprezen.kaizen.oasparser.model3.Schema; import com.reprezen.kaizen.oasparser.model3.Server; import com.sun.jersey.api.uri.UriTemplate; +import com.sun.jersey.api.uri.UriTemplateParser; /** * @author Lyn Goltz @@ -27,6 +31,8 @@ public class OpenApiUtils { // as described in https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#fixed-fields private static final String DEFAULT_SERVER_URL = "/"; + public static final String TEMPLATE = "\\{.*\\}"; + private OpenApiUtils() { } @@ -54,7 +60,8 @@ public static List retrieveTestPoints( OpenApi3 apiModel ) { * @return the parsed test points, may be empty but never null */ public static List retrieveTestPoints( OpenApi3 apiModel, PATH path ) { - return retrieveTestPoints( apiModel, path, null ); + String requestedPath = "/" + path.getPathItem(); + return retrieveTestPoints( apiModel, requestedPath ); } /** @@ -63,21 +70,66 @@ public static List retrieveTestPoints( OpenApi3 apiModel, PATH path ) * * @param apiModel * never null - * @param path - * the path the test points should be assigned to, never null - * @param extendedPath + * @param collectionName * the extended path, may be null * @return the parsed test points, may be empty but never null */ - public static List retrieveTestPoints( OpenApi3 apiModel, PATH path, String extendedPath ) { + public static List retrieveTestPointsForCollectionMetadata( OpenApi3 apiModel, String collectionName ) { StringBuilder requestedPath = new StringBuilder(); - requestedPath.append( path.getPathItem() ); - if ( extendedPath != null ) { - if ( !extendedPath.startsWith( "/" ) ) - requestedPath.append( "/" ); - requestedPath.append( extendedPath ); - } - List pathItemObjects = identifyTestPoints( apiModel, requestedPath.toString() ); + requestedPath.append( "/" ); + requestedPath.append( COLLECTIONS.getPathItem() ); + requestedPath.append( "/" ); + requestedPath.append( collectionName ); + + return retrieveTestPoints( apiModel, requestedPath.toString() ); + } + + /** + * Parse the test points with the passed path including the extended path from the passed OpenApi3 document as + * described in A.4.3. Identify the Test Points. + * + * @param apiModel + * never null + * @param collectionName + * the extended path, may be null + * @return the parsed test points, may be empty but never null + */ + public static List retrieveTestPointsForCollection( OpenApi3 apiModel, String collectionName ) { + StringBuilder requestedPath = new StringBuilder(); + requestedPath.append( "/" ); + requestedPath.append( COLLECTIONS.getPathItem() ); + requestedPath.append( "/" ); + requestedPath.append( collectionName ); + requestedPath.append( "/items" ); + + return retrieveTestPoints( apiModel, requestedPath.toString() ); + } + + /** + * Parse the test points with the passed path including the extended path from the passed OpenApi3 document as + * described in A.4.3. Identify the Test Points. + * + * @param apiModel + * never null + * @param collectionName + * the extended path, may be null + * @return the parsed test points, may be empty but never null + */ + public static List retrieveTestPointsForFeature( OpenApi3 apiModel, String collectionName, + String featureId ) { + StringBuilder requestedPath = new StringBuilder(); + requestedPath.append( "/" ); + requestedPath.append( COLLECTIONS.getPathItem() ); + requestedPath.append( "/" ); + requestedPath.append( collectionName ); + requestedPath.append( "/items/" ); + requestedPath.append( featureId ); + + return retrieveTestPoints( apiModel, requestedPath.toString() ); + } + + private static List retrieveTestPoints( OpenApi3 apiModel, String requestedPath ) { + List pathItemObjects = identifyTestPoints( apiModel, requestedPath ); List pathItemAndServers = identifyServerUrls( apiModel, pathItemObjects ); return processServerObjects( pathItemAndServers ); } @@ -107,17 +159,13 @@ public static List retrieveTestPoints( OpenApi3 apiModel, PATH path, * never null */ private static List identifyTestPoints( OpenApi3 apiModel ) { - List paths = new ArrayList<>(); + List allTestPoints = new ArrayList<>(); for ( PATH path : PATH.values() ) - paths.add( path.getPathItem() ); - return identifyTestPoints( apiModel, paths ); + allTestPoints.addAll( identifyTestPoints( apiModel, "/" + path.getPathItem() ) ); + return allTestPoints; } private static List identifyTestPoints( OpenApi3 apiModel, String path ) { - return identifyTestPoints( apiModel, Collections.singletonList( path ) ); - } - - private static List identifyTestPoints( OpenApi3 apiModel, List path ) { List pathItems = new ArrayList<>(); Map pathItemObjects = apiModel.getPaths(); for ( Path pathItemObject : pathItemObjects.values() ) { @@ -129,12 +177,10 @@ private static List identifyTestPoints( OpenApi3 apiModel, List pa return pathItems; } - private static boolean isRequestedPath( String pathString, List path ) { - for ( String pathRequested : path ) { - if ( pathString.matches( "\\/" + pathRequested + "(\\/?)" ) ) - return true; - } - return false; + private static boolean isRequestedPath( String pathUnderTest, String pathToMatch ) { + UriTemplateParser parser = new UriTemplateParser( pathUnderTest ); + Matcher matcher = parser.getPattern().matcher( pathToMatch ); + return matcher.matches(); } /** @@ -249,23 +295,24 @@ private static List processServerObjects( List pat private static void processServerObject( List uris, PathItemAndServer pathItemAndServer ) { String pathString = pathItemAndServer.pathItemObject.getPathString(); - UriTemplate uriTemplate = new UriTemplate( pathItemAndServer.serverUrl + pathString ); Response response = pathItemAndServer.operationObject.getResponse( "200" ); Map contentMediaTypes = response.getContentMediaTypes(); + UriTemplate uriTemplate = new UriTemplate( pathItemAndServer.serverUrl + pathString ); if ( uriTemplate.getNumberOfTemplateVariables() == 0 ) { - TestPoint testPoint = new TestPoint( uriTemplate, contentMediaTypes ); + TestPoint testPoint = new TestPoint( pathItemAndServer.serverUrl, pathString, contentMediaTypes ); uris.add( testPoint ); } else { List> templateReplacements = collectTemplateReplacements( pathItemAndServer, uriTemplate ); if ( templateReplacements.isEmpty() ) { - TestPoint testPoint = new TestPoint( uriTemplate, contentMediaTypes ); + TestPoint testPoint = new TestPoint( pathItemAndServer.serverUrl, pathString, contentMediaTypes ); uris.add( testPoint ); } else { for ( Map templateReplacement : templateReplacements ) { - TestPoint testPoint = new TestPoint( uriTemplate, templateReplacement, contentMediaTypes ); + TestPoint testPoint = new TestPoint( pathItemAndServer.serverUrl, pathString, templateReplacement, + contentMediaTypes ); uris.add( testPoint ); } } diff --git a/src/main/java/org/opengis/cite/wfs30/openapi3/TestPoint.java b/src/main/java/org/opengis/cite/wfs30/openapi3/TestPoint.java index 29b343af..69d34d15 100644 --- a/src/main/java/org/opengis/cite/wfs30/openapi3/TestPoint.java +++ b/src/main/java/org/opengis/cite/wfs30/openapi3/TestPoint.java @@ -1,12 +1,11 @@ package org.opengis.cite.wfs30.openapi3; -import java.util.HashMap; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import com.reprezen.kaizen.oasparser.model3.MediaType; -import com.sun.jersey.api.uri.UriTemplate; /** * Encapsulates a Test Point with the UriTemplate and predefined replacements. @@ -15,69 +14,70 @@ */ public class TestPoint { - private UriTemplate uriTemplate; + private final String serverUrl; - private Map templateReplacement; + private final String path; + + private Map predefinedTemplateReplacement; private List requirementClasses; private Map contentMediaTypes; /** - * Instantiates a TestPoint with UriTemplate but without defined replacements. + * Instantiates a TestPoint with UriTemplate but without predefined replacements. * - * @param uriTemplate - * never null + * @param serverUrl + * the serverUrl, never null + * @param path + * the path never, null * @param contentMediaTypes * the content media types for the GET operation with response "200", may be null */ - public TestPoint( UriTemplate uriTemplate, Map contentMediaTypes ) { - this( uriTemplate, null, contentMediaTypes ); + public TestPoint( String serverUrl, String path, Map contentMediaTypes ) { + this( serverUrl, path, Collections.emptyMap(), contentMediaTypes ); } /** * Instantiates a TestPoint with UriTemplate and predefined replacements. * - * @param uriTemplate - * never null - * @param templateReplacement - * may be null + * @param serverUrl + * the serverUrl, never null + * @param path + * the path, never null + * @param predefinedTemplateReplacement + * a list of predefined replacements never null * @param contentMediaTypes * the content media types for the GET operation with response "200", may be null */ - public TestPoint( UriTemplate uriTemplate, Map templateReplacement, + public TestPoint( String serverUrl, String path, Map predefinedTemplateReplacement, Map contentMediaTypes ) { - this.uriTemplate = uriTemplate; - this.templateReplacement = templateReplacement; + this.serverUrl = serverUrl; + this.path = path; + this.predefinedTemplateReplacement = Collections.unmodifiableMap( predefinedTemplateReplacement ); this.contentMediaTypes = contentMediaTypes; } /** - * @return the UriTemplate, never null + * + * @return the serverUrl never null */ - public UriTemplate getUriTemplate() { - return uriTemplate; + public String getServerUrl() { + return serverUrl; } /** - * @return predefined replacements, may be null + * @return the path never, null */ - public Map getTemplateReplacement() { - return templateReplacement; + public String getPath() { + return path; } /** - * Adds a new template replacement - * - * @param key - * the key of the template, never null - * @param value - * the value of the template, never null + * @return an unmodifiable mao with predefined replacements, may be empty but never null */ - public void addTemplateReplacement( String key, String value ) { - if ( this.templateReplacement == null ) - this.templateReplacement = new HashMap<>(); - this.templateReplacement.put( key, value ); + public Map getPredefinedTemplateReplacement() { + return predefinedTemplateReplacement; } /** @@ -105,20 +105,9 @@ public Map getContentMediaTypes() { return contentMediaTypes; } - /** - * Creates an URI from the template with the replacement. - * - * @return the URI created from the template, never null - */ - public String createUri() { - if ( templateReplacement != null ) - return uriTemplate.createURI( templateReplacement ); - return uriTemplate.createURI(); - } - @Override public String toString() { - return "Pattern: " + uriTemplate.getPattern() + ", Replacements: " + templateReplacement; + return "Server URL: " + serverUrl + " , Path: " + path + ", Replacements: " + predefinedTemplateReplacement; } @Override @@ -128,15 +117,16 @@ public boolean equals( Object o ) { if ( o == null || getClass() != o.getClass() ) return false; TestPoint testPoint = (TestPoint) o; - return Objects.equals( uriTemplate, testPoint.uriTemplate ) - && Objects.equals( templateReplacement, testPoint.templateReplacement ) + return Objects.equals( serverUrl, testPoint.serverUrl ) + && Objects.equals( path, testPoint.predefinedTemplateReplacement ) + && Objects.equals( predefinedTemplateReplacement, testPoint.path ) && Objects.equals( requirementClasses, testPoint.requirementClasses ) && Objects.equals( contentMediaTypes, testPoint.contentMediaTypes ); } @Override public int hashCode() { - return Objects.hash( uriTemplate, templateReplacement, requirementClasses, contentMediaTypes ); + return Objects.hash( serverUrl, path, predefinedTemplateReplacement, requirementClasses, contentMediaTypes ); } } diff --git a/src/main/java/org/opengis/cite/wfs30/openapi3/UriBuilder.java b/src/main/java/org/opengis/cite/wfs30/openapi3/UriBuilder.java new file mode 100644 index 00000000..94614c6d --- /dev/null +++ b/src/main/java/org/opengis/cite/wfs30/openapi3/UriBuilder.java @@ -0,0 +1,88 @@ +package org.opengis.cite.wfs30.openapi3; + +import java.util.HashMap; +import java.util.Map; + +import com.sun.jersey.api.uri.UriTemplate; +import com.sun.jersey.api.uri.UriTemplateParser; + +/** + * Builds a URL out of a TestPoint. + * + * @author Lyn Goltz + */ +public class UriBuilder { + + private final TestPoint testPoint; + + private final Map templateReplacements = new HashMap<>(); + + /** + * @param testPoint + * never null + */ + public UriBuilder( TestPoint testPoint ) { + this.testPoint = testPoint; + this.templateReplacements.putAll( testPoint.getPredefinedTemplateReplacement() ); + } + + /** + * Adds the collectionName to the URI + * + * @param collectionName + * never null + * @return this UrlBuilder + */ + public UriBuilder collectionName( String collectionName ) { + String templateName = retrieveCollectionNameTemplateName(); + addTemplateReplacement( collectionName, templateName ); + return this; + } + + /** + * Adds the featureId to the URI + * + * @param featureId + * never null + * @return this UrlBuilder + */ + public UriBuilder featureId( String featureId ) { + String templateName = retrieveFeatureIdTemplateName(); + addTemplateReplacement( featureId, templateName ); + return this; + } + + /** + * @return this URI, never null + */ + public String buildUrl() { + UriTemplate uriTemplate = new UriTemplate( testPoint.getServerUrl() + testPoint.getPath() ); + return uriTemplate.createURI( templateReplacements ); + } + + private void addTemplateReplacement( String collectionName, String templateName ) { + if ( templateName != null ) + templateReplacements.put( templateName, collectionName ); + } + + private String retrieveCollectionNameTemplateName() { + String path = testPoint.getPath(); + UriTemplateParser uriTemplateParser = new UriTemplateParser( path ); + for ( String templateName : uriTemplateParser.getNames() ) { + if ( path.startsWith( "/collections/{" + templateName + "}" ) ) + return templateName; + } + return null; + } + + private String retrieveFeatureIdTemplateName() { + String path = testPoint.getPath(); + UriTemplateParser uriTemplateParser = new UriTemplateParser( path ); + for ( String templateName : uriTemplateParser.getNames() ) { + if ( path.endsWith( "items/{" + templateName + "}" ) ) + return templateName; + } + return null; + } + +} \ No newline at end of file diff --git a/src/test/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperationIT.java b/src/test/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperationIT.java index 5cc57821..c8808555 100644 --- a/src/test/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperationIT.java +++ b/src/test/java/org/opengis/cite/wfs30/collections/FeatureCollectionsMetadataOperationIT.java @@ -19,7 +19,6 @@ import com.reprezen.kaizen.oasparser.OpenApi3Parser; import com.reprezen.kaizen.oasparser.model3.MediaType; import com.reprezen.kaizen.oasparser.model3.OpenApi3; -import com.sun.jersey.api.uri.UriTemplate; /** * @author Lyn Goltz @@ -50,10 +49,8 @@ public static void initTestFixture() public void testValidateFeatureCollectionsMetadataOperationResponse() { FeatureCollectionsMetadataOperation featureCollectionsMetadataOperation = new FeatureCollectionsMetadataOperation(); featureCollectionsMetadataOperation.initCommonFixture( testContext ); - featureCollectionsMetadataOperation.parseRequiredMetadata( testContext ); featureCollectionsMetadataOperation.openApiDocument( testContext ); - UriTemplate conformanceUri = new UriTemplate( "https://www.ldproxy.nrw.de/kataster/collections" ); - TestPoint testPoint = new TestPoint( conformanceUri, mediaTypes() ); + TestPoint testPoint = new TestPoint( "https://www.ldproxy.nrw.de/kataster", "/collections", mediaTypes() ); featureCollectionsMetadataOperation.validateFeatureCollectionsMetadataOperation( testPoint ); featureCollectionsMetadataOperation.validateFeatureCollectionsMetadataOperationResponse_Links( testPoint ); featureCollectionsMetadataOperation.validateFeatureCollectionsMetadataOperationResponse_Collections( testPoint ); @@ -64,7 +61,8 @@ public void testValidateFeatureCollectionsMetadataOperationResponse() { Map collection = (Map) object[1]; featureCollectionsMetadataOperation.validateCollectionsMetadataDocument_Links( tp, collection ); featureCollectionsMetadataOperation.validateCollectionsMetadataDocument_Extent( tp, collection ); - featureCollectionsMetadataOperation.validateTheFeatureCollectionMetadataOperationAndResponse( tp, collection ); + featureCollectionsMetadataOperation.validateTheFeatureCollectionMetadataOperationAndResponse( tp, + collection ); } } diff --git a/src/test/java/org/opengis/cite/wfs30/collections/GetFeatureOperationIT.java b/src/test/java/org/opengis/cite/wfs30/collections/GetFeatureOperationIT.java index a72b1156..75577e1f 100644 --- a/src/test/java/org/opengis/cite/wfs30/collections/GetFeatureOperationIT.java +++ b/src/test/java/org/opengis/cite/wfs30/collections/GetFeatureOperationIT.java @@ -77,15 +77,7 @@ public void testGetFeatureOperations() { assertThat( featureId, notNullValue() ); getFeatureOperation.getFeatureOperation( collection, featureId ); - - Iterator iterator = getFeatureOperation.collectionItemUris( testContext ); - Object[] collectionByName = findCollectionByName( COLLECTION_NAME, iterator ); - assertThat( collectionByName, notNullValue() ); - - Map collection2 = (Map) collectionByName[0]; - assertThat( collection2, notNullValue() ); - - getFeatureOperation.validateTheGetFeatureOperationResponse( collection2 ); + getFeatureOperation.validateTheGetFeatureOperationResponse( collection, featureId ); } private Object[] findCollectionByName( String collectionName, Iterator collections ) { diff --git a/src/test/java/org/opengis/cite/wfs30/conformance/ConformanceOperationIT.java b/src/test/java/org/opengis/cite/wfs30/conformance/ConformanceOperationIT.java index 2e38170b..5cb34dc3 100644 --- a/src/test/java/org/opengis/cite/wfs30/conformance/ConformanceOperationIT.java +++ b/src/test/java/org/opengis/cite/wfs30/conformance/ConformanceOperationIT.java @@ -16,7 +16,6 @@ import org.testng.ITestContext; import com.reprezen.kaizen.oasparser.model3.MediaType; -import com.sun.jersey.api.uri.UriTemplate; /** * @author Lyn Goltz @@ -42,8 +41,7 @@ public static void initTestFixture() public void testApiDefinition() { ConformanceOperation conformanceOperation = new ConformanceOperation(); conformanceOperation.initCommonFixture( testContext ); - UriTemplate conformanceUri = new UriTemplate( "https://www.ldproxy.nrw.de/kataster/conformance" ); - TestPoint testPoint = new TestPoint( conformanceUri, mediaTypes() ); + TestPoint testPoint = new TestPoint( "https://www.ldproxy.nrw.de/kataster", "/conformance", mediaTypes() ); conformanceOperation.validateConformanceOperationAndResponse( testPoint ); } diff --git a/src/test/java/org/opengis/cite/wfs30/openapi3/OpenApiUtilsTest.java b/src/test/java/org/opengis/cite/wfs30/openapi3/OpenApiUtilsTest.java index 7e460e77..5f4bf2eb 100644 --- a/src/test/java/org/opengis/cite/wfs30/openapi3/OpenApiUtilsTest.java +++ b/src/test/java/org/opengis/cite/wfs30/openapi3/OpenApiUtilsTest.java @@ -5,6 +5,9 @@ import static org.opengis.cite.wfs30.WFS3.PATH.API; import static org.opengis.cite.wfs30.WFS3.PATH.COLLECTIONS; import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPoints; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForCollection; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForCollectionMetadata; +import static org.opengis.cite.wfs30.openapi3.OpenApiUtils.retrieveTestPointsForFeature; import java.net.URL; import java.util.List; @@ -46,23 +49,23 @@ public void testRetrieveTestPoints_moreComplex() { assertThat( testPoints.size(), is( 4 ) ); TestPoint testPointWIthIndex = testPoints.get( 0 ); - assertThat( testPointWIthIndex.getTemplateReplacement().size(), is( 1 ) ); - assertThat( testPointWIthIndex.getTemplateReplacement().get( "index" ), is( "10" ) ); + assertThat( testPointWIthIndex.getPredefinedTemplateReplacement().size(), is( 1 ) ); + assertThat( testPointWIthIndex.getPredefinedTemplateReplacement().get( "index" ), is( "10" ) ); TestPoint testPointWIthIndexAndEnum1 = testPoints.get( 1 ); - assertThat( testPointWIthIndexAndEnum1.getTemplateReplacement().size(), is( 2 ) ); - assertThat( testPointWIthIndexAndEnum1.getTemplateReplacement().get( "index" ), is( "10" ) ); - assertThat( testPointWIthIndexAndEnum1.getTemplateReplacement().get( "enum" ), is( "eins" ) ); + assertThat( testPointWIthIndexAndEnum1.getPredefinedTemplateReplacement().size(), is( 2 ) ); + assertThat( testPointWIthIndexAndEnum1.getPredefinedTemplateReplacement().get( "index" ), is( "10" ) ); + assertThat( testPointWIthIndexAndEnum1.getPredefinedTemplateReplacement().get( "enum" ), is( "eins" ) ); TestPoint testPointWIthIndexAndEnum2 = testPoints.get( 2 ); - assertThat( testPointWIthIndexAndEnum2.getTemplateReplacement().size(), is( 2 ) ); - assertThat( testPointWIthIndexAndEnum2.getTemplateReplacement().get( "index" ), is( "10" ) ); - assertThat( testPointWIthIndexAndEnum2.getTemplateReplacement().get( "enum" ), is( "zwei" ) ); + assertThat( testPointWIthIndexAndEnum2.getPredefinedTemplateReplacement().size(), is( 2 ) ); + assertThat( testPointWIthIndexAndEnum2.getPredefinedTemplateReplacement().get( "index" ), is( "10" ) ); + assertThat( testPointWIthIndexAndEnum2.getPredefinedTemplateReplacement().get( "enum" ), is( "zwei" ) ); TestPoint testPointWIthIndexAndEnum3 = testPoints.get( 3 ); - assertThat( testPointWIthIndexAndEnum3.getTemplateReplacement().size(), is( 2 ) ); - assertThat( testPointWIthIndexAndEnum3.getTemplateReplacement().get( "index" ), is( "10" ) ); - assertThat( testPointWIthIndexAndEnum3.getTemplateReplacement().get( "enum" ), is( "drei" ) ); + assertThat( testPointWIthIndexAndEnum3.getPredefinedTemplateReplacement().size(), is( 2 ) ); + assertThat( testPointWIthIndexAndEnum3.getPredefinedTemplateReplacement().get( "index" ), is( "10" ) ); + assertThat( testPointWIthIndexAndEnum3.getPredefinedTemplateReplacement().get( "enum" ), is( "drei" ) ); } @Test @@ -90,38 +93,125 @@ public void testRetrieveTestPoints_COLLECTIONS() { } @Test - public void testRetrieveTestPoints_COLLECTIONS_WithExtendedPath() { + public void testRetrieveTestPointsForCollectionMetadata() { OpenApi3Parser parser = new OpenApi3Parser(); URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi.json" ); OpenApi3 apiModel = parser.parse( openAppiDocument, true ); - List testPoints = retrieveTestPoints( apiModel, COLLECTIONS, "flurstueck" ); + List testPoints = retrieveTestPointsForCollectionMetadata( apiModel, "flurstueck" ); assertThat( testPoints.size(), is( 1 ) ); TestPoint testPoint = testPoints.get( 0 ); - assertThat( testPoint.createUri(), - is( "http://www.ldproxy.nrw.de/rest/services/kataster/collections/flurstueck" ) ); + // assertThat( testPoint.createUri(), + // is( "http://www.ldproxy.nrw.de/rest/services/kataster/collections/flurstueck" ) ); + assertThat( testPoint.getServerUrl(), is( "http://www.ldproxy.nrw.de/rest/services/kataster" ) ); + assertThat( testPoint.getPath(), is( "/collections/flurstueck" ) ); + + Map contentMediaTypes = testPoint.getContentMediaTypes(); + assertThat( contentMediaTypes.size(), is( 2 ) ); + } + + @Test + public void testRetrieveTestPointsForCollection() { + OpenApi3Parser parser = new OpenApi3Parser(); + + URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi.json" ); + OpenApi3 apiModel = parser.parse( openAppiDocument, true ); + List testPoints = retrieveTestPointsForCollection( apiModel, "flurstueck" ); + + assertThat( testPoints.size(), is( 1 ) ); + + TestPoint testPoint = testPoints.get( 0 ); + // assertThat( testPoint.createUri(), + // is( "http://www.ldproxy.nrw.de/rest/services/kataster/collections/flurstueck/items" ) ); + assertThat( testPoint.getServerUrl(), is( "http://www.ldproxy.nrw.de/rest/services/kataster" ) ); + assertThat( testPoint.getPath(), is( "/collections/flurstueck/items" ) ); + Map contentMediaTypes = testPoint.getContentMediaTypes(); assertThat( contentMediaTypes.size(), is( 2 ) ); } @Test - public void testRetrieveTestPoints_COLLECTIONS_WithRegEx() { + public void testRetrieveTestPointsForFeature() { OpenApi3Parser parser = new OpenApi3Parser(); URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi.json" ); OpenApi3 apiModel = parser.parse( openAppiDocument, true ); - List testPoints = retrieveTestPoints( apiModel, COLLECTIONS, "flurstueck\\/items\\/\\{.*\\}" ); + List testPoints = retrieveTestPointsForFeature( apiModel, "flurstueck", "abc" ); assertThat( testPoints.size(), is( 1 ) ); TestPoint testPoint = testPoints.get( 0 ); - testPoint.addTemplateReplacement( "featureId", "abc" ); - assertThat( testPoint.createUri(), - is( "http://www.ldproxy.nrw.de/rest/services/kataster/collections/flurstueck/items/abc" ) ); + assertThat( testPoint.getServerUrl(), is( "http://www.ldproxy.nrw.de/rest/services/kataster" ) ); + assertThat( testPoint.getPath(), is( "/collections/flurstueck/items/{featureId}" ) ); Map contentMediaTypes = testPoint.getContentMediaTypes(); assertThat( contentMediaTypes.size(), is( 2 ) ); } + @Test + public void testRetrieveTestPoints_COLLECTIONS_compactAPI() { + OpenApi3Parser parser = new OpenApi3Parser(); + + URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi_compact-api.json" ); + OpenApi3 apiModel = parser.parse( openAppiDocument, true ); + List testPoints = retrieveTestPoints( apiModel, COLLECTIONS ); + + assertThat( testPoints.size(), is( 1 ) ); + + TestPoint testPoint = testPoints.get( 0 ); + // assertThat( testPoint.createUri(), is( "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3/collections" ) ); + + assertThat( testPoint.getServerUrl(), is( "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3" ) ); + assertThat( testPoint.getPath(), is( "/collections" ) ); + + assertThat( testPoint.getContentMediaTypes().size(), is( 4 ) ); + } + + @Test + public void testRetrieveTestPointsForCollectionMetadata_compactAPI() { + OpenApi3Parser parser = new OpenApi3Parser(); + + URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi_compact-api.json" ); + OpenApi3 apiModel = parser.parse( openAppiDocument, true ); + List testPoints = retrieveTestPointsForCollectionMetadata( apiModel, "test__countries" ); + + assertThat( testPoints.size(), is( 1 ) ); + + TestPoint testPoint = testPoints.get( 0 ); + + assertThat( testPoint.getServerUrl(), is( "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3" ) ); + assertThat( testPoint.getPath(), is( "/collections/{collectionId}" ) ); + } + + @Test + public void testRetrieveTestPointsForCollection_compactAPI() { + OpenApi3Parser parser = new OpenApi3Parser(); + + URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi_compact-api.json" ); + OpenApi3 apiModel = parser.parse( openAppiDocument, true ); + List testPoints = retrieveTestPointsForCollection( apiModel, "test__countries" ); + + assertThat( testPoints.size(), is( 1 ) ); + + TestPoint testPoint = testPoints.get( 0 ); + assertThat( testPoint.getServerUrl(), is( "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3" ) ); + assertThat( testPoint.getPath(), is( "/collections/{collectionId}/items" ) ); + } + + @Test + public void testRetrieveTestPointsForFeature_compactAPI() { + OpenApi3Parser parser = new OpenApi3Parser(); + + URL openAppiDocument = OpenApiUtilsTest.class.getResource( "openapi_compact-api.json" ); + OpenApi3 apiModel = parser.parse( openAppiDocument, true ); + List testPoints = retrieveTestPointsForFeature( apiModel, "test__countries", "abc" ); + + assertThat( testPoints.size(), is( 1 ) ); + + TestPoint testPoint = testPoints.get( 0 ); + assertThat( testPoint.getServerUrl(), is( "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3" ) ); + assertThat( testPoint.getPath(), is( "/collections/{collectionId}/items/{featureId}" ) ); + } + } diff --git a/src/test/java/org/opengis/cite/wfs30/openapi3/UrlBuilderTest.java b/src/test/java/org/opengis/cite/wfs30/openapi3/UrlBuilderTest.java new file mode 100644 index 00000000..c5010180 --- /dev/null +++ b/src/test/java/org/opengis/cite/wfs30/openapi3/UrlBuilderTest.java @@ -0,0 +1,75 @@ +package org.opengis.cite.wfs30.openapi3; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Collections; + +import org.junit.Test; + +/** + * @author Lyn Goltz + */ +public class UrlBuilderTest { + + @Test + public void testBuildUrl_collectionMetadata_withTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/{name}", Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "water" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/water" ) ); + } + + @Test + public void testBuildUrl_collectionMetadata_withoutTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/forest", Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "forest" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/forest" ) ); + } + + @Test + public void testBuildUrl_collection_withTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/{name}/items", + Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "water" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/water/items" ) ); + } + + @Test + public void testBuildUrl_collection_withoutTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/forest/items", + Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "forest" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/forest/items" ) ); + } + + @Test + public void testBuildUrl_feature_withFeatureIdTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/forest/items/{featureId}", + Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "forest" ).featureId( "1" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/forest/items/1" ) ); + } + + @Test + public void testBuildUrl_feature_withCollectionNameAndFeatureIdTemplate() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/{name}/items/{featureId}", + Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "water" ).featureId( "2" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/water/items/2" ) ); + } + + @Test + public void testBuildUrl_feature_withoutTemplates() { + TestPoint tp = new TestPoint( "http://localhost:8080/service", "/collections/forest/items/3", + Collections.emptyMap() ); + String url = new UriBuilder( tp ).collectionName( "forest" ).featureId( "3" ).buildUrl(); + + assertThat( url, is( "http://localhost:8080/service/collections/forest/items/3" ) ); + } +} diff --git a/src/test/resources/org/opengis/cite/wfs30/openapi3/openapi_compact-api.json b/src/test/resources/org/opengis/cite/wfs30/openapi3/openapi_compact-api.json new file mode 100644 index 00000000..9b2eb04d --- /dev/null +++ b/src/test/resources/org/opengis/cite/wfs30/openapi3/openapi_compact-api.json @@ -0,0 +1,930 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "WFS 3.0 server", + "contact": { + "name": "Andrea Aime - GeoSolutions", + "email": "andrea.aime@geo-solutions.it" + }, + "version": "2.14-SNAPSHOT" + }, + "externalDocs": { + "description": "WFS specification", + "url": "https://github.com/opengeospatial/WFS_FES" + }, + "servers": [ + { + "url": "http://cloudsdi.geo-solutions.it:80/geoserver/wfs3", + "description": "This server" + } + ], + "tags": [ + { + "name": "Capabilities", + "description": "Essential characteristics of this API including information about the data." + }, + { + "name": "Features", + "description": "Access to data (features)." + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "Capabilities" + ], + "summary": "landing page of this API", + "description": "The landing page provides links to the API definition, the Conformance statements and the metadata about the feature data in this dataset.", + "operationId": "getLandingPage", + "responses": { + "200": { + "description": "links to the API capabilities", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/root" + } + }, + "text/html": { + "schema": { + "type": "string" + } + }, + "application/openapi+json;version=3.0": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml": { + "schema": { + "type": "string" + } + }, + "application/x-yaml": { + "schema": { + "$ref": "#/components/schemas/root" + } + } + } + } + } + } + }, + "/conformance": { + "get": { + "tags": [ + "Capabilities" + ], + "summary": "information about standards that this API conforms to", + "description": "list all requirements classes specified in a standard (e.g., WFS 3.0 Part 1: Core) that the server conforms to", + "operationId": "getRequirementsClasses", + "responses": { + "200": { + "description": "the URIs of all requirements classes supported by the server", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/req-classes" + } + }, + "text/xml": { + "schema": { + "type": "string" + } + }, + "application/x-yaml": { + "schema": { + "$ref": "#/components/schemas/req-classes" + } + } + } + }, + "default": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + } + } + } + } + } + }, + "/collections": { + "get": { + "tags": [ + "Capabilities" + ], + "summary": "describe the feature collections in the dataset", + "operationId": "describeCollections", + "responses": { + "200": { + "description": "Metdata about the feature collections shared by this API.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/content" + } + }, + "text/html": { + "schema": { + "type": "string" + } + }, + "text/xml": { + "schema": { + "type": "string" + } + }, + "application/x-yaml": { + "schema": { + "$ref": "#/components/schemas/content" + } + } + } + }, + "default": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/collections/{collectionId}": { + "get": { + "tags": [ + "Capabilities" + ], + "summary": "describe the {collectionId} feature collection", + "operationId": "describeCollection", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + } + ], + "responses": { + "200": { + "description": "Metadata about the {collectionId} collection shared by this API.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionInfo" + } + }, + "text/html": { + "schema": { + "type": "string" + } + }, + "text/xml": { + "schema": { + "type": "string" + } + }, + "application/x-yaml": { + "schema": { + "$ref": "#/components/schemas/collectionInfo" + } + } + } + }, + "default": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/collections/{collectionId}/items": { + "get": { + "tags": [ + "Features" + ], + "summary": "retrieve features of feature collection {collectionId}", + "description": "Every feature in a dataset belongs to a collection. A dataset may consist of multiple feature collections. A feature collection is often a collection of features of a similar type, based on a common schema.\\\nUse content negotiation to request HTML or GeoJSON.", + "operationId": "getFeatures", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/bbox" + }, + { + "$ref": "#/components/parameters/time" + } + ], + "responses": { + "200": { + "description": "Information about the feature collection plus the first features matching the selection parameters.", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/featureCollectionGeoJSON" + } + }, + "text/html": { + "schema": { + "type": "string" + } + }, + "application/gml+xml; version=3.2": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/2.1.2": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/3.2": { + "schema": { + "type": "string" + } + }, + "application/vnd.google-earth.kml+xml": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "application/vnd.google-earth.kml xml": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/3.1.1": { + "schema": { + "type": "string" + } + } + } + }, + "default": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/collections/{collectionId}/items/{featureId}": { + "get": { + "tags": [ + "Features" + ], + "summary": "retrieve a feature; use content negotiation to request HTML or GeoJSON", + "operationId": "getFeature", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId" + }, + { + "$ref": "#/components/parameters/featureId" + } + ], + "responses": { + "200": { + "description": "A feature.", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/featureGeoJSON" + } + }, + "text/html": { + "schema": { + "type": "string" + } + }, + "application/gml+xml; version=3.2": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/2.1.2": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/3.2": { + "schema": { + "type": "string" + } + }, + "application/vnd.google-earth.kml+xml": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "application/vnd.google-earth.kml xml": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/xml; subtype=gml/3.1.1": { + "schema": { + "type": "string" + } + } + } + }, + "default": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "exception": { + "required": [ + "code" + ], + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "root": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "type": "array", + "example": [ + { + "href": "http://data.example.org/", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/api", + "rel": "service", + "type": "application/openapi+json;version=3.0", + "title": "the API definition" + }, + { + "href": "http://data.example.org/conformance", + "rel": "conformance", + "type": "application/json", + "title": "WFS 3.0 conformance classes implemented by this server" + }, + { + "href": "http://data.example.org/collections", + "rel": "data", + "type": "application/json", + "title": "Metadata about the feature collections" + } + ], + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "req-classes": { + "required": [ + "conformsTo" + ], + "type": "object", + "properties": { + "conformsTo": { + "type": "array", + "example": [ + "http://www.opengis.net/spec/wfs-1/3.0/req/core", + "http://www.opengis.net/spec/wfs-1/3.0/req/oas30", + "http://www.opengis.net/spec/wfs-1/3.0/req/html", + "http://www.opengis.net/spec/wfs-1/3.0/req/geojson" + ], + "items": { + "type": "string" + } + } + } + }, + "link": { + "required": [ + "href" + ], + "type": "object", + "properties": { + "href": { + "type": "string" + }, + "rel": { + "type": "string", + "example": "prev" + }, + "type": { + "type": "string", + "example": "application/geo+json" + }, + "hreflang": { + "type": "string", + "example": "en" + } + } + }, + "content": { + "required": [ + "collections", + "links" + ], + "type": "object", + "properties": { + "links": { + "type": "array", + "example": [ + { + "href": "http://data.example.org/collections.json", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/collections.html", + "rel": "alternate", + "type": "text/html", + "title": "this document as HTML" + }, + { + "href": "http://schemas.example.org/1.0/foobar.xsd", + "rel": "describedBy", + "type": "application/xml", + "title": "XML schema for Acme Corporation data" + } + ], + "items": { + "$ref": "#/components/schemas/link" + } + }, + "collections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/collectionInfo" + } + } + } + }, + "collectionInfo": { + "required": [ + "links", + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "identifier of the collection used, for example, in URIs", + "example": "buildings" + }, + "title": { + "type": "string", + "description": "human readable title of the collection", + "example": "Buildings" + }, + "description": { + "type": "string", + "description": "a description of the features in the collection", + "example": "Buildings in the city of Bonn." + }, + "links": { + "type": "array", + "example": [ + { + "href": "http://data.example.org/collections/buildings/items", + "rel": "item", + "type": "application/geo+json", + "title": "Buildings" + }, + { + "href": "http://example.org/concepts/building.html", + "rel": "describedBy", + "type": "text/html", + "title": "Feature catalogue for buildings" + } + ], + "items": { + "$ref": "#/components/schemas/link" + } + }, + "extent": { + "$ref": "#/components/schemas/extent" + }, + "crs": { + "type": "array", + "description": "The coordinate reference systems in which geometries may be retrieved. Coordinate reference systems are identified by a URI. The first coordinate reference system is the coordinate reference system that is used by default. This is always \"http://www.opengis.net/def/crs/OGC/1.3/CRS84\", i.e. WGS84 longitude/latitude.", + "items": { + "type": "string" + }, + "default": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ] + } + } + }, + "extent": { + "type": "object", + "properties": { + "crs": { + "type": "string", + "description": "Coordinate reference system of the coordinates in the spatial extent (property `spatial`). In the Core, only WGS84 longitude/latitude is supported. Extensions may support additional coordinate reference systems.", + "enum": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ], + "default": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "spatial": { + "maxItems": 6, + "minItems": 4, + "type": "array", + "description": "West, north, east, south edges of the spatial extent. The minimum and maximum values apply to the coordinate reference system WGS84 longitude/latitude that is supported in the Core. If, for example, a projected coordinate reference system is used, the minimum and maximum values need to be adjusted.", + "example": [ + -180, + -90, + 180, + 90 + ], + "items": { + "type": "number" + } + }, + "trs": { + "type": "string", + "description": "Temporal reference system of the coordinates in the temporal extent (property `temporal`). In the Core, only the Gregorian calendar is supported. Extensions may support additional temporal reference systems.", + "enum": [ + "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + ], + "default": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + }, + "temporal": { + "maxItems": 2, + "minItems": 2, + "type": "array", + "description": "Begin and end times of the temporal extent.", + "example": [ + "2011-11-11T12:22:11Z", + "2012-11-24T12:32:43Z" + ], + "items": { + "type": "string", + "format": "dateTime" + } + } + } + }, + "featureCollectionGeoJSON": { + "required": [ + "features", + "type" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "FeatureCollection" + ] + }, + "features": { + "type": "array", + "items": { + "$ref": "#/components/schemas/featureGeoJSON" + } + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "dateTime" + }, + "numberMatched": { + "minimum": 0, + "type": "integer" + }, + "numberReturned": { + "minimum": 0, + "type": "integer" + } + } + }, + "featureGeoJSON": { + "required": [ + "geometry", + "properties", + "type" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "Feature" + ] + }, + "geometry": { + "$ref": "#/components/schemas/geometryGeoJSON" + }, + "properties": { + "type": "object", + "nullable": true + }, + "id": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + } + }, + "geometryGeoJSON": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "Point", + "MultiPoint", + "LineString", + "MultiLineString", + "Polygon", + "MultiPolygon", + "GeometryCollection" + ] + } + } + } + }, + "parameters": { + "limit": { + "name": "limit", + "in": "query", + "description": "The optional limit parameter limits the number of items that are\npresented in the response document.\n\nOnly items are counted that are on the first level of the collection in\nthe response document. Nested objects contained within the explicitly\nrequested items shall not be counted.\n", + "required": false, + "style": "form", + "explode": false, + "schema": { + "maximum": 1000000, + "minimum": 1, + "type": "integer", + "default": 1000000 + } + }, + "bbox": { + "name": "bbox", + "in": "query", + "description": "Only features that have a geometry that intersects the bounding box are selected. The bounding box is provided as four or six numbers, depending on whether the coordinate reference system includes a vertical axis (elevation or depth):\n* Lower left corner, coordinate axis 1 * Lower left corner, coordinate axis 2 * Lower left corner, coordinate axis 3 (optional) * Upper right corner, coordinate axis 1 * Upper right corner, coordinate axis 2 * Upper right corner, coordinate axis 3 (optional)\nThe coordinate reference system of the values is WGS84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a different coordinate reference system is specified in the parameter `bbox-crs`.\nFor WGS84 longitude/latitude the values are in most cases the sequence of minimum longitude, minimum latitude, maximum longitude and maximum latitude. However, in cases where the box spans the antimeridian the first value (west-most box edge) is larger than the third value (east-most box edge).\nIf a feature has multiple spatial geometry properties, it is the decision of the server whether only a single spatial geometry property is used to determine the extent or all relevant geometries.\n", + "required": false, + "style": "form", + "explode": false, + "schema": { + "maxItems": 6, + "minItems": 4, + "type": "array", + "items": { + "type": "number" + } + } + }, + "time": { + "name": "time", + "in": "query", + "description": "Either a date-time or a period string that adheres to RFC 3339. Examples:\n* A date-time: \"2018-02-12T23:20:50Z\" * A period: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\" or \"2018-02-12T00:00:00Z/P1M6DT12H31M12S\"\nOnly features that have a temporal property that intersects the value of `time` are selected.\nIf a feature has multiple temporal properties, it is the decision of the server whether only a single temporal property is used to determine the extent or all relevant temporal properties.", + "required": false, + "style": "form", + "explode": false, + "schema": { + "type": "string" + } + }, + "collectionId": { + "name": "collectionId", + "in": "path", + "description": "Identifier (name) of a specific collection", + "required": true, + "schema": { + "type": "string", + "enum": [ + "eumetsat__ne_10m_coastline", + "eumetsat__ne_boundary_lines_land", + "eumetsat__wind_ascat", + "eumetsat__wind_ascat_reduced", + "eumetsat__wind_ascat_thinned", + "landsat8__B3_index", + "geosolutions__LANDSAT8__B1", + "geosolutions__SENTINEL1_V2", + "geosolutions__SENTINEL1__MOSAIC_B1", + "geosolutions__SENTINEL1__vv", + "geosolutions__SENTINEL2_V2", + "geosolutions__SENTINEL2__B04", + "geosolutions__SENTINEL2__MOSAIC_B01", + "geosolutions__product", + "geosolutions__smb_simple_points", + "geosolutions__testing", + "zaatari__AgricultureSrf", + "zaatari__CultureSrf", + "zaatari__FacilitySrf", + "zaatari__HydrographySrf", + "zaatari__InformationPnt", + "zaatari__RecreationSrf", + "zaatari__SettlementSrf", + "zaatari__StructurePnt", + "zaatari__StructureSrf", + "zaatari__TransportationGroundCrv", + "zaatari__TransportationGroundSrf", + "zaatari__UtilityInfrastructureSrf", + "zaatari__o2s_A", + "zaatari__o2s_L", + "zaatari__osm_points", + "zaatari__zaatari_worldview_timeseries_index", + "geoedge__administrative_subdivision_s", + "geoedge__anchorage_p", + "geoedge__building_p", + "geoedge__built_up_area_p", + "geoedge__cemetery_s", + "geoedge__code_list_t", + "geoedge__conservation_area_s", + "geoedge__dam_c", + "geoedge__dam_s", + "geoedge__dataset_s", + "geoedge__dataset_t", + "geoedge__entity_collection_metadata_s", + "geoedge__entity_collection_metadata_t", + "geoedge__foreshore_s", + "geoedge__forest_s", + "geoedge__gauging_station_p", + "geoedge__heliport_p", + "geoedge__inland_waterbody_s", + "geoedge__land_aerodrome_p", + "geoedge__land_water_boundary_c", + "geoedge__maritime_limit_c", + "geoedge__military_installation_s", + "geoedge__navigable_canal_s", + "geoedge__park_s", + "geoedge__pipeline_c", + "geoedge__port_p", + "geoedge__reef_c", + "geoedge__restriction_info_t", + "geoedge__river_c", + "geoedge__river_s", + "geoedge__road_c", + "geoedge__rock_formation_p", + "geoedge__runway_s", + "geoedge__soil_surface_region_s", + "geoedge__trail_c", + "geoedge__tunnel_c", + "geoedge__water_aerodrome_p", + "geoedge__water_well_p", + "test__countries", + "test__eo_collection", + "test__eo_product", + "test__granule", + "sf__AggregateGeoFeature", + "sf__EntitéGénérique", + "sf__PrimitiveGeoFeature", + "daraa__AgriculturePnt", + "daraa__AgricultureSrf", + "daraa__Cultivated_2011", + "daraa__Cultivated_2012", + "daraa__Cultivated_2013", + "daraa__CulturePnt", + "daraa__CultureSrf", + "daraa__FacilityPnt", + "daraa__FacilitySrf", + "daraa__FieldBoundary_2011", + "daraa__Health_2011", + "daraa__Health_2012", + "daraa__Health_2013", + "daraa__HydrographyCrv", + "daraa__HydrographySrf", + "daraa__InformationPnt", + "daraa__Inventory_2011", + "daraa__Inventory_2012", + "daraa__Inventory_2013", + "daraa__MilitarySrf", + "daraa__RecreationPnt", + "daraa__RecreationSrf", + "daraa__SettlementPnt", + "daraa__SettlementSrf", + "daraa__StructureCrv", + "daraa__StructurePnt", + "daraa__StructureSrf", + "daraa__TransportationGroundCrv", + "daraa__TransportationGroundPnt", + "daraa__TransportationGroundSrf", + "daraa__UtilityInfrastructureCrv", + "daraa__UtilityInfrastructurePnt", + "daraa__VegetationSrf", + "daraa__daraa_landsat8_timeseries_index", + "daraa__daraa_worldview_timeseries_index", + "daraa__o2s_A", + "daraa__o2s_L", + "daraa__o2s_P", + "osm__public_gns_iceland_version_log", + "osm__public_osm_administrative_version_log", + "osm__public_osm_protected_area_version_log" + ] + } + }, + "featureId": { + "name": "featureId", + "in": "path", + "description": "Local identifier of a specific feature", + "required": true, + "schema": { + "type": "string" + } + } + } + } +} \ No newline at end of file