Skip to content

Commit

Permalink
Style operations on single collection too
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Jan 24, 2019
1 parent 9a8989a commit 4bedc4c
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 62 deletions.
4 changes: 2 additions & 2 deletions src/community/wfs3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ Implementation wise:
* The module basically acts as an internal proxy around WFS 2.0, using a servlet filter to adapt protocols. The long term approach would likely be to have a new MVCDispatcher that allows usage of Spring annotations instead (TBD).

This implementation contains the following prototype WFS3 extensions:
* Tiles extension, returning MapBOX/JSON/TopoJSON tiles
* Styles extension, with the ability to get/put/delete styles (must be secured using service security)
* Tiles extension, returning MapBOX/JSON/TopoJSON tiles. It does not cache tiles and will likely be removed when WFS3 is pushed to supported land, but served as a base to study a possible WMTS 2.0 API and WFS 3 tile extension in Testbed 14. Some bits can likely be re-used once a final version comes out.
* Styles extension, with the ability to get/put/delete styles (must be secured using service security). The API is not really compatible with GeoServer style management and should also be removed, but was used to provide feedback to OGC in the vector tiles pilot, which will likely be used for WMTS 2.0 (and some bits can likely be re-used once a final version comes out).
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.NamespaceInfo;
Expand Down Expand Up @@ -229,7 +232,8 @@ public void setApplicationContext(ApplicationContext context) throws BeansExcept
}

@Override
public void postStyles(HttpServletRequest request, HttpServletResponse response)
public void postStyles(
HttpServletRequest request, HttpServletResponse response, PostStyleRequest post)
throws IOException {
final String mimeType = request.getContentType();
final StyleHandler handler = Styles.handler(mimeType);
Expand Down Expand Up @@ -276,13 +280,28 @@ public void postStyles(HttpServletRequest request, HttpServletResponse response)

catalog.add(sinfo);

// do we need to associate with a layer?
LayerInfo layer = null;
if (post.getLayerName() != null) {
// layer existence already checked in WFS3Filter
layer = getCatalog().getLayerByName(post.getLayerName());
layer.getStyles().add(sinfo);
getCatalog().save(layer);
}

// build and return the new path
ResponseUtils.appendPath(request.getContextPath());
final String baseURL = ResponseUtils.baseURL(request);
final String url =
ResponseUtils.buildURL(
// URLType.SERVICE is important here, otherwise no ws localization
baseURL, "wfs3/styles/" + name, null, SERVICE);
final String path =
layer == null
? "wfs3/styles/" + name
: "wfs3/collections/"
+ NCNameResourceCodec.encode(layer.getResource())
+ "/styles/"
+ name;
// URLType.SERVICE is important here, otherwise no ws localization
final String url = ResponseUtils.buildURL(baseURL, path, null, SERVICE);

response.setStatus(HttpStatus.CREATED.value());
response.addHeader(HttpHeaders.LOCATION, url);
}
Expand All @@ -300,34 +319,55 @@ public String getStyleName(StyledLayerDescriptor sld) {
public StylesDocument getStyles(GetStylesRequest request) throws IOException {
List<StyleDocument> styles = new ArrayList<>();

// return only styles that are not associated to a layer, those will show up
// in the layer association instead
final Set<StyleInfo> blacklist = getLayerAssociatedStyles();
addBuiltInStyles(blacklist);
for (StyleInfo style : getCatalog().getStyles()) {
if (blacklist.contains(style)) {
continue;
if (request.getLayerName() == null) {
// return only styles that are not associated to a layer, those will show up
// in the layer association instead
final Set<StyleInfo> blacklist = getLayerAssociatedStyles();
addBuiltInStyles(blacklist);
for (StyleInfo style : getCatalog().getStyles()) {
if (blacklist.contains(style)) {
continue;
}
StyleDocument sd = buildStyleDocument(request, style);

styles.add(sd);
}
StyleDocument sd = StyleDocument.build(style);
String styleFormat = style.getFormat();
if (styleFormat == null) {
styleFormat = "SLD";
} else {
final LayerInfo layer = getCatalog().getLayerByName(request.getLayerName());
if (layer.getDefaultStyle() != null) {
StyleDocument sd = buildStyleDocument(request, layer.getDefaultStyle());
styles.add(sd);
}
// canonicalize
styleFormat = Styles.handler(styleFormat).mimeType(null);
// add link for native format
sd.addLink(buildLink(request, sd, styleFormat));
if (!SLDHandler.MIMETYPE_10.equalsIgnoreCase(styleFormat)) {
// add link for SLD 1.0 translation
sd.addLink(buildLink(request, sd, SLDHandler.MIMETYPE_10));
if (layer.getStyles() != null) {
for (StyleInfo style : layer.getStyles()) {
if (style != null) {
StyleDocument sd = buildStyleDocument(request, style);
styles.add(sd);
}
}
}

styles.add(sd);
}

return new StylesDocument(styles);
}

public StyleDocument buildStyleDocument(GetStylesRequest request, StyleInfo style) {
StyleDocument sd = StyleDocument.build(style);
String styleFormat = style.getFormat();
if (styleFormat == null) {
styleFormat = "SLD";
}
// canonicalize
styleFormat = Styles.handler(styleFormat).mimeType(null);
// add link for native format
sd.addLink(buildLink(request, sd, styleFormat));
if (!SLDHandler.MIMETYPE_10.equalsIgnoreCase(styleFormat)) {
// add link for SLD 1.0 translation
sd.addLink(buildLink(request, sd, SLDHandler.MIMETYPE_10));
}
return sd;
}

public void addBuiltInStyles(Set<StyleInfo> blacklist) {
accumulateStyle(blacklist, getCatalog().getStyleByName(StyleInfo.DEFAULT_POINT));
accumulateStyle(blacklist, getCatalog().getStyleByName(StyleInfo.DEFAULT_LINE));
Expand All @@ -337,10 +377,18 @@ public void addBuiltInStyles(Set<StyleInfo> blacklist) {
}

public Link buildLink(GetStylesRequest request, StyleDocument sd, String styleFormat) {
String path;
if (request.getLayerName() != null) {
FeatureTypeInfo featureType = getCatalog().getFeatureTypeByName(request.getLayerName());
String collectionId = NCNameResourceCodec.encode(featureType);
path = "wfs3/collections/" + collectionId + "/styles/" + sd.getId();
} else {
path = "wfs3/styles/" + sd.getId();
}
String href =
ResponseUtils.buildURL(
request.getBaseUrl(),
"wfs3/styles/" + sd.getId(),
path,
Collections.singletonMap("f", styleFormat),
SERVICE);
return new Link(href, "style", styleFormat, null);
Expand Down Expand Up @@ -373,10 +421,32 @@ private void accumulateStyle(Set<StyleInfo> result, StyleInfo style) {

@Override
public StyleInfo getStyle(GetStyleRequest request) throws IOException {
final StyleInfo style = getCatalog().getStyleByName(request.getStyleId());
StyleInfo style = null;
LayerInfo layer = null;
if (request.getLayerName() != null) {
// layer existence already checked in WFSFilter
layer = getCatalog().getLayerByName(request.getLayerName());
if (layer.getDefaultStyle() != null
&& layer.getDefaultStyle().getName().equals(request.getStyleId())) {
style = layer.getDefaultStyle();
} else {
Predicate<StyleInfo> styleFilter =
s -> s != null && s.getName().equalsIgnoreCase(request.getStyleId());
Optional<StyleInfo> first =
layer.getStyles().stream().filter(styleFilter).findFirst();
if (first.isPresent()) {
style = first.get();
}
}
} else {
style = getCatalog().getStyleByName(request.getStyleId());
}
if (style == null) {
throw new HttpErrorCodeException(
NOT_FOUND.value(), "Style " + request.getStyleId() + " could not be found");
String message = "Style " + request.getStyleId() + " could not be found";
if (layer != null) {
message += " in collection " + NCNameResourceCodec.encode(layer.getResource());
}
throw new HttpErrorCodeException(NOT_FOUND.value(), message);
}

return style;
Expand All @@ -397,13 +467,13 @@ public void putStyle(
final String styleBody = IOUtils.toString(request.getReader());

final WorkspaceInfo wsInfo = LocalWorkspace.get();
String name = putStyle.getStyleId();
StyleInfo sinfo = catalog.getStyleByName(wsInfo, name);
String styleName = putStyle.getStyleId();
StyleInfo sinfo = catalog.getStyleByName(wsInfo, styleName);
boolean newStyle = sinfo == null;
if (newStyle) {
sinfo = catalog.getFactory().createStyle();
sinfo.setName(name);
sinfo.setFilename(name + "." + handler.getFileExtension());
sinfo.setName(styleName);
sinfo.setFilename(styleName + "." + handler.getFileExtension());
}

sinfo.setFormat(handler.getFormat());
Expand All @@ -425,6 +495,17 @@ public void putStyle(
catalog.save(sinfo);
}

final String layerName = putStyle.getLayerName();
if (layerName != null) {
final LayerInfo layer = catalog.getLayerByName(layerName);
if (!layer.getStyles()
.stream()
.anyMatch(s -> s != null && s.getName().equalsIgnoreCase(styleName))) {
layer.getStyles().add(sinfo);
catalog.save(layer);
}
}

response.setStatus(NO_CONTENT.value());
}

Expand All @@ -433,12 +514,33 @@ public void deleteStyle(DeleteStyleRequest request, HttpServletResponse response
throws IOException {
String name = request.getStyleId();
WorkspaceInfo ws = LocalWorkspace.get();
StyleInfo sinfo = getCatalog().getStyleByName(ws, name);
final Catalog catalog = getCatalog();
StyleInfo sinfo = catalog.getStyleByName(ws, name);
if (sinfo == null) {
throw new HttpErrorCodeException(
NOT_FOUND.value(), "Could not find style with id: " + name);
}
getCatalog().remove(sinfo);

if (request.getLayerName() != null) {
LayerInfo layer = catalog.getLayerByName(request.getLayerName());
if (sinfo.equals(layer.getDefaultStyle())) {
StyleInfo newDefault =
new CatalogBuilder(catalog).getDefaultStyle(layer.getResource());
layer.setDefaultStyle(newDefault);
} else {
if (!(layer.getStyles().remove(sinfo))) {
throw new HttpErrorCodeException(
NOT_FOUND.value(),
"Style with id: "
+ name
+ " is not associated to collection "
+ NCNameResourceCodec.encode(layer.getResource()));
}
}
catalog.save(layer);
}

catalog.remove(sinfo);

response.setStatus(NO_CONTENT.value());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class DeleteStyleRequest extends BaseRequest {

String styleId;

String collectionId;
String layerName;

public String getStyleId() {
return styleId;
Expand All @@ -19,11 +19,11 @@ public void setStyleId(String styleId) {
this.styleId = styleId;
}

public String getCollectionId() {
return collectionId;
public String getLayerName() {
return layerName;
}

public void setCollectionId(String collectionId) {
this.collectionId = collectionId;
public void setLayerName(String layerName) {
this.layerName = layerName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class GetStyleRequest extends BaseRequest {

String styleId;

String collectionId;
String layerName;

public String getStyleId() {
return styleId;
Expand All @@ -19,11 +19,11 @@ public void setStyleId(String styleId) {
this.styleId = styleId;
}

public String getCollectionId() {
return collectionId;
public String getLayerName() {
return layerName;
}

public void setCollectionId(String collectionId) {
this.collectionId = collectionId;
public void setLayerName(String layerName) {
this.layerName = layerName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,15 @@
package org.geoserver.wfs3;

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

String layerName;

public String getLayerName() {
return layerName;
}

public void setLayerName(String layerName) {
this.layerName = layerName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* (c) 201 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;

public class PostStyleRequest extends BaseRequest {
String layerName;

public String getLayerName() {
return layerName;
}

public void setLayerName(String layerName) {
this.layerName = layerName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class PutStyleRequest extends BaseRequest {
String styleId;
String collectionId;
String layerName;

public String getStyleId() {
return styleId;
Expand All @@ -16,11 +16,11 @@ public void setStyleId(String styleId) {
this.styleId = styleId;
}

public String getCollectionId() {
return collectionId;
public String getLayerName() {
return layerName;
}

public void setCollectionId(String collectionId) {
this.collectionId = collectionId;
public void setLayerName(String layerName) {
this.layerName = layerName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ public Object operationExecuted(Request request, Operation operation, Object res
} else if ("api".equals(request.getRequest())) {
defaultType = OpenAPIResponse.OPEN_API_MIME;
}
setOutputFormat(request, parsedRequest, formatSetter, defaultType);
// for getStyle we're going to use the "native" format if possible
if (!"getStyle".equals(request.getRequest())) {
setOutputFormat(request, parsedRequest, formatSetter, defaultType);
}
}
}
} catch (Exception e) {
Expand Down

0 comments on commit 4bedc4c

Please sign in to comment.