Skip to content

Commit

Permalink
HTML UI for API and some fixes for HTML pages links in proxied servers
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Aug 6, 2018
1 parent 2355d41 commit 100ed56
Show file tree
Hide file tree
Showing 16 changed files with 395 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateMethodModel;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.Response;
import org.geoserver.ows.URLMangler;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
import org.geoserver.template.TemplateUtils;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs3.BaseRequest;
import org.geoserver.wfs3.GetFeatureType;

public abstract class AbstractHTMLResponse extends Response {

Expand Down Expand Up @@ -50,20 +55,77 @@ public void write(Object value, OutputStream output, Operation operation)
Template template = templateSupport.getTemplate(ri, templateName);

try {
HashMap<String, Object> model = new HashMap<>();
model.put("model", value);
model.put("service", geoServer.getService(WFSInfo.class));
model.put("contact", geoServer.getGlobal().getSettings().getContact());
model.put("baseURL", getBaseURL(value, operation));
HashMap<String, Object> model = setupModel(value, operation);
template.process(model, new OutputStreamWriter(output));
} catch (TemplateException e) {
throw new IOException("Error occured processing HTML template " + templateName, e);
}
}

protected String getBaseURL(Object value, Operation operation) {
BaseRequest request = (BaseRequest) operation.getParameters()[0];
return request.getBaseUrl();
/**
* Prepares the model with the usual suspects:
*
* <ul>
* <li>model: the model object
* <li>service: the WFSInfo object
* <li>contact: the contact information
* <li>baseURL: the GeoServer baseURL for link construction
* </ul>
*
* @param value
* @param operation
* @return
*/
protected HashMap<String, Object> setupModel(Object value, Operation operation) {
HashMap<String, Object> model = new HashMap<>();
model.put("model", value);
model.put("service", geoServer.getService(WFSInfo.class));
model.put("contact", geoServer.getGlobal().getSettings().getContact());
model.put("baseURL", getBaseURL(operation));
addServiceLinkFunctions(value, operation, model);
return model;
}

static void addServiceLinkFunctions(
Object value, Operation operation, Map<String, Object> model) {
model.put(
"serviceLink",
(TemplateMethodModel)
arguments ->
ResponseUtils.buildURL(
getBaseURL(operation),
(String) arguments.get(0),
null,
URLMangler.URLType.SERVICE));
model.put(
"resourceLink",
(TemplateMethodModel)
arguments ->
ResponseUtils.buildURL(
getBaseURL(operation),
(String) arguments.get(0),
null,
URLMangler.URLType.RESOURCE));
model.put(
"externalLink",
(TemplateMethodModel)
arguments ->
ResponseUtils.buildURL(
getBaseURL(operation),
(String) arguments.get(0),
null,
URLMangler.URLType.EXTERNAL));
}

static String getBaseURL(Operation operation) {
Object firstParam = operation.getParameters()[0];
if (firstParam instanceof BaseRequest) {
BaseRequest request = (BaseRequest) firstParam;
return request.getBaseUrl();
} else if (firstParam instanceof GetFeatureType) {
return ((GetFeatureType) firstParam).getBaseUrl();
}
throw new IllegalArgumentException("Cannot extract base URL from " + firstParam);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ protected void write(
Map<String, Object> model = new HashMap<>();
model.put("baseURL", request.getBaseURL());
model.put("response", response);
AbstractHTMLResponse.addServiceLinkFunctions(response, getFeature, model);

try (OutputStreamWriter osw = new OutputStreamWriter(output)) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* (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.response;

import io.swagger.v3.oas.models.OpenAPI;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.platform.GeoServerResourceLoader;

/** Returns a swagger-UI HTML wrapper for the JSON response */
public class OpenAPIHTMLResponse extends AbstractHTMLResponse {

public OpenAPIHTMLResponse(GeoServerResourceLoader loader, GeoServer geoServer) {
super(OpenAPI.class, loader, geoServer);
}

@Override
protected String getTemplateName(Object value) {
return "api.ftl";
}

@Override
protected ResourceInfo getResource(Object value) {
return null;
}
}
5 changes: 5 additions & 0 deletions src/community/wfs3/src/main/resources/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
<bean id="openAPIResponse" class="org.geoserver.wfs3.response.OpenAPIResponse">
<constructor-arg ref="geoServer"/>
</bean>
<bean id="openAPIHTMLResponse" class="org.geoserver.wfs3.response.OpenAPIHTMLResponse">
<constructor-arg ref="resourceLoader"/>
<constructor-arg ref="geoServer"/>
</bean>
<bean id="landingPageResponse" class="org.geoserver.wfs3.response.LandingPageResponse">
<constructor-arg ref="geoServer"/>
</bean>
Expand Down Expand Up @@ -129,6 +133,7 @@
<property name="mappings">
<props>
<prop key="/wfs3css/**">classpathPublisher</prop>
<prop key="/swagger-ui/**">classpathPublisher</prop>
</props>
</property>
</bean>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="${resourceLink('swagger-ui/swagger-ui.css')}" >
<link rel="icon" type="image/png" href="${resourceLink('swagger-ui/favicon-32x32.png')}" sizes="32x32" />
<link rel="icon" type="image/png" href="${resourceLink('swagger-ui/favicon-16x16.png')}" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>

<body>
<div id="swagger-ui"></div>

<script src="${resourceLink('swagger-ui/swagger-ui-bundle.js')}"> </script>
<script src="${resourceLink('swagger-ui/swagger-ui-standalone-preset.js')}"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "${serviceLink('wfs3/api?f=json')}",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
This document is also available as <#list model.getLinksExcept(null, "text/html") as link><a href="${link.href}">${link.type}</a><#if link_has_next>, </#if></#list>.</p>

<#list model.collections as collection>
<h2><a href="${baseURL}wfs3/collections/${collection.name}">${collection.name}</a></h2>
<h2><a href="${serviceLink("wfs3/collections/${collection.name}")}">${collection.name}</a></h2>
<#include "collection_include.ftl">
</#list>
<#include "common-footer.ftl">
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<#setting locale="en_US">
<html>
<head>
<link rel="stylesheet" href="${baseURL}wfs3css/blueprint/screen.css" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${baseURL}wfs3css/blueprint/print.css" type="text/css" media="print" />
<link rel="stylesheet" href="${baseURL}wfs3css/geoserver.css" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${baseURL}wfs3css/blueprint/ie.css" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${resourceLink("wfs3css/blueprint/screen.css")}" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${resourceLink("wfs3css/blueprint/print.css")}" type="text/css" media="print" />
<link rel="stylesheet" href="${resourceLink("wfs3css/geoserver.css")}" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="${resourceLink("wfs3css/blueprint/ie.css")}" type="text/css" media="screen, projection" />
</head>
<body>
<div id="header">
<a href="${baseURL}wfs3"></a>
<a href="${serviceLink("wfs3")}"></a>
</div>
<div id="content">
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->
<#if collection??>
<#-- Expended only in WFS3 -->
<h2><a href="${baseURL}wfs3/collections/${collection}">${collection}</a></h2>
<h2><a href="${serviceLink("wfs3/collections/${collection}")}"></a></h2>
<#else>
<h2>${data.type.name}</h2>
</#if>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>

<h2>API definition</h2>
<p>The <a id="jsonApiLink" href="${model.getLinkUrl('api', 'application/json')!}"> API document</a> provides a machine processable description of this service API conformant to OpenAPI 3.
<p>The <a id="jsonApiLink" href="${model.getLinkUrl('api', 'text/html')!}"> API document</a> provides a machine processable description of this service API conformant to OpenAPI 3.
<br/>
This API document is also available as
<#list model.getLinksExcept("api", "application/json") as link><a href="${link.href}">${link.type}</a><#if link_has_next>, </#if></#list>.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!doctype html>
<html lang="en-US">
<body onload="run()">
</body>
</html>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;

if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}

arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}

isValid = qp.state === sentState

if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
});
}

if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}

oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
</script>

Large diffs are not rendered by default.

0 comments on commit 100ed56

Please sign in to comment.