Skip to content

Commit

Permalink
Adding contents request support
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Mar 7, 2018
1 parent e39b952 commit f2dd5f1
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 89 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* <p> * <p>
* Actually, found this after implementing the class, https://github * Actually, found this after implementing the class, https://github
* .com/swagger-api/swagger-core/tree/master/modules/swagger-models. * .com/swagger-api/swagger-core/tree/master/modules/swagger-models.
* It might be used for re-implementing this class in a cleaner, less error prone and more general way (although, * It might be used for re-implementing this class in a cleaner, less error prone and more general way (although,
* think about * think about
* streaming support for large API documents too) * streaming support for large API documents too)
*/ */
Expand Down Expand Up @@ -695,21 +695,16 @@ protected Map<String, Schema> getSchemas() {
} }


protected Map<String, FormatDescription> getAvailableFormats() { protected Map<String, FormatDescription> getAvailableFormats() {
Map<String, FormatDescription> descriptions = new TreeMap<>(); Map<String, FormatDescription> descriptions = new LinkedHashMap<>();
Collection featureProducers = GeoServerExtensions.extensions(WFSGetFeatureOutputFormat.class); List<String> formatNames = DefaultWebFeatureService30.getAvailableFormats();
for (Iterator i = featureProducers.iterator(); i.hasNext(); ) { for (String formatName: formatNames) {
WFSGetFeatureOutputFormat format = (WFSGetFeatureOutputFormat) i.next(); if ((formatName.contains("text") && !formatName.contains("xml") && !formatName.contains("gml")) ||
// TODO: get better collaboration from content formatName.contains("csv")) {
Set<String> formats = format.getOutputFormats();
if (formats.isEmpty()) {
continue;
}
String formatName = formats.iterator().next();
if (formatName.contains("text") || formatName.contains("csv")) {
descriptions.put(formatName, new FormatDescription().withType(TYPE_STRING)); descriptions.put(formatName, new FormatDescription().withType(TYPE_STRING));
} else { } else {
descriptions.put(formatName, new FormatDescription().withType(TYPE_OBJECT)); descriptions.put(formatName, new FormatDescription().withType(TYPE_OBJECT));
} }

} }
return descriptions; return descriptions;
} }
Expand Down
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,12 @@
/* (c) 2018 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wfs3;

/**
* Request for the server contents
*/
public class ContentRequest extends BaseRequest {

}
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,235 @@
package org.geoserver.wfs3;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.util.CloseableIterator;
import org.geoserver.ows.URLMangler;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.ServiceException;
import org.geoserver.wfs.WFSInfo;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.filter.Filter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* A class representing the WFS3 server "contents" in a way that Jackson can easily translate to JSON/YAML (and
* can be used as a Freemarker template model)
*/
public class ContentsDocument {

public static final String REL_SELF = "self";
public static final String REL_ALTERNATE = "alternate";
public static final String REL_SERVICE = "service";
public static final String REL_ABOUT = "about";
public static final String REL_DESCRIBEDBY = "describedBy";

static class Link {
String href;
String rel;
String type;
String title;

public Link() {
}

public Link(String href, String rel, String type, String title) {
this.href = href;
this.rel = rel;
this.type = type;
this.title = title;
}

public String getHref() {
return href;
}

public void setHref(String href) {
this.href = href;
}

public String getRel() {
return rel;
}

public void setRel(String rel) {
this.rel = rel;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}
}

static class Collection {
String collectionId;
String title;
String description;
double[] extent;
List<Link> links = new ArrayList<>();

public String getCollectionId() {
return collectionId;
}

public void setCollectionId(String collectionId) {
this.collectionId = collectionId;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public double[] getExtent() {
return extent;
}

public void setExtent(double[] extent) {
this.extent = extent;
}

public List<Link> getLinks() {
return links;
}

public void addLink(Link link) {
links.add(link);
}

}

private final Catalog catalog;
private final WFSInfo wfs;
private final List<Link> links = new ArrayList<>();
private final ContentRequest request;

public ContentsDocument(ContentRequest request, WFSInfo wfs, Catalog catalog) {
this.wfs = wfs;
this.catalog = catalog;
this.request = request;

// TODO: make this pluggable based on the available responses

// setting up the contents link
String baseUrl = request.getBaseUrl();
String contentsUrl = ResponseUtils.buildURL(baseUrl, "wfs3/", null, URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(contentsUrl, ContentsDocument.REL_SELF, BaseRequest.JSON_MIME, "This" +
" document"));
String contentsHtmlUrl = ResponseUtils.buildURL(baseUrl, "wfs3/", Collections.singletonMap("f", "html"),
URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(contentsHtmlUrl, ContentsDocument.REL_ALTERNATE, BaseRequest
.HTML_MIME, "This document as HTML"));
String contentsYamlUrl = ResponseUtils.buildURL(baseUrl, "wfs3/", Collections.singletonMap("f", "yaml"),
URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(contentsYamlUrl, ContentsDocument.REL_ALTERNATE, BaseRequest
.YAML_MIME, "This document as YAML"));

// setting up the API links
String apiUrl = ResponseUtils.buildURL(baseUrl, "wfs3/api", null, URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(apiUrl, ContentsDocument.REL_SERVICE, BaseRequest.JSON_MIME, "The " +
"OpenAPI definition as JSON"));
String apiHtmlUrl = ResponseUtils.buildURL(baseUrl, "wfs3/api", Collections.singletonMap("f", "html"),
URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(apiHtmlUrl, ContentsDocument.REL_SERVICE, BaseRequest.HTML_MIME,
"The OpenAPI definition as HTML"));
String apiYamlUrl = ResponseUtils.buildURL(baseUrl, "wfs3/api", Collections.singletonMap("f", "yaml"),
URLMangler.URLType.SERVICE);
addLink(new ContentsDocument.Link(apiYamlUrl, ContentsDocument.REL_SERVICE, BaseRequest.YAML_MIME,
"The OpenAPI definition as YAML"));
}

public void addLink(Link link) {
links.add(link);
}

public List<Link> getLinks() {
return links;
}

public Iterator<Collection> getCollections() {
CloseableIterator<FeatureTypeInfo> featureTypes = catalog.list(FeatureTypeInfo.class, Filter.INCLUDE);
return new Iterator<Collection>() {

Collection next;

@Override
public boolean hasNext() {
if (next != null) {
return true;
}

boolean hasNext = featureTypes.hasNext();
if (!hasNext) {
featureTypes.close();
return false;
} else {
try {
FeatureTypeInfo featureType = featureTypes.next();
next = mapToCollection(featureType);
return true;
} catch(Exception e) {
featureTypes.close();
throw new ServiceException("Failed to iterate over the feature types in the catalog", e);
}
}
}

@Override
public Collection next() {
Collection result = next;
this.next = null;
return result;
}
};
}

private Collection mapToCollection(FeatureTypeInfo featureType) {
Collection collection = new Collection();

// basic info
String collectionId = NCNameResourceCodec.encode(featureType);
collection.setCollectionId(collectionId);
collection.setTitle(featureType.getTitle());
collection.setDescription(featureType.getDescription());
ReferencedEnvelope bbox = featureType.getLatLonBoundingBox();
collection.setExtent(new double[] {bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox.getMaxY()});

// links
List<String> formats = DefaultWebFeatureService30.getAvailableFormats();
String baseUrl = request.getBaseUrl();
for (String format : formats) {
String apiUrl = ResponseUtils.buildURL(baseUrl, "wfs3/" + collectionId, Collections.singletonMap("f", format), URLMangler.URLType.SERVICE);
collection.addLink(new ContentsDocument.Link(apiUrl, ContentsDocument.REL_ABOUT, format, collectionId + " as " + format));
}

return collection;
}

}
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@


import net.opengis.wfs20.GetFeatureType; import net.opengis.wfs20.GetFeatureType;
import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServer;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wfs.WFSGetFeatureOutputFormat;
import org.geoserver.wfs.WFSInfo; import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.WebFeatureService20; import org.geoserver.wfs.WebFeatureService20;
import org.geoserver.wfs.request.FeatureCollectionResponse; import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.opengis.filter.FilterFactory2; import org.opengis.filter.FilterFactory2;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/** /**
* WFS 3.0 implementation * WFS 3.0 implementation
*/ */
Expand All @@ -34,16 +42,49 @@ public void setFilterFactory(FilterFactory2 filterFactory) {
this.filterFactory = filterFactory; this.filterFactory = filterFactory;
} }


@Override
public ContentsDocument contents(ContentRequest request) {
ContentsDocument contents = new ContentsDocument(request, geoServer.getService(WFSInfo.class), geoServer
.getCatalog());
return contents;
}

@Override @Override
public APIDocument api(APIRequest request) { public APIDocument api(APIRequest request) {
return new APIDocument(geoServer.getService(WFSInfo.class), geoServer.getCatalog()); return new APIDocument(geoServer.getService(WFSInfo.class), geoServer.getCatalog());
} }


@Override @Override
public FeatureCollectionResponse getFeature(GetFeatureType request) { public FeatureCollectionResponse getFeature(GetFeatureType request) {



return wfs20.getFeature(request); return wfs20.getFeature(request);
} }

/**
* Returns a selection of supported formats favouring
*
* @return
*/
public static List<String> getAvailableFormats() {
Set<String> formatNames = new LinkedHashSet<>();
Collection featureProducers = GeoServerExtensions.extensions(WFSGetFeatureOutputFormat.class);
for (Iterator i = featureProducers.iterator(); i.hasNext(); ) {
WFSGetFeatureOutputFormat format = (WFSGetFeatureOutputFormat) i.next();
// TODO: get better collaboration from content
Set<String> formats = format.getOutputFormats();
if (formats.isEmpty()) {
continue;
}
// try to get a MIME type, otherwise pick the first available
String formatName = formats.stream()
.filter(f -> f.contains("/"))
.findFirst()
.orElse(formats.iterator().next());
// hack to skip over the JSONP format, not recognizable as is
if ("json".equals(formatName)) {
continue;
}
formatNames.add(formatName);
}
return new ArrayList<>(formatNames);
}
} }

0 comments on commit f2dd5f1

Please sign in to comment.