You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2018/02/27 10:48:36 UTC
[5/7] qpid-broker-j git commit: QPID-8103: [Broker-J] [WMC] [Query
UI] Add ability to download query results as CSV
QPID-8103: [Broker-J] [WMC] [Query UI] Add ability to download query results as CSV
(cherry picked from commit a2920afbc2ce92345c18c1add3e6d310358d4c69)
Project: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/commit/686712e8
Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/686712e8
Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/686712e8
Branch: refs/heads/7.0.x
Commit: 686712e83fd21c3e569e3e317bc462dcbf5ebdcd
Parents: 94e1fd6
Author: Alex Rudyy <or...@apache.org>
Authored: Mon Feb 26 16:19:21 2018 +0000
Committer: Alex Rudyy <or...@apache.org>
Committed: Tue Feb 27 10:48:02 2018 +0000
----------------------------------------------------------------------
QPID-8066.tar.bz2 | Bin 0 -> 16303 bytes
.../server/management/plugin/csv/CSVFormat.java | 22 +++++++++-
.../plugin/servlet/rest/AbstractServlet.java | 23 ++++++++++
.../plugin/servlet/rest/QueryServlet.java | 44 ++++++++++++++++---
.../plugin/servlet/rest/RestServlet.java | 22 ----------
.../src/main/java/resources/css/common.css | 4 ++
.../resources/js/qpid/management/Management.js | 17 +++++--
.../js/qpid/management/query/QueryWidget.js | 31 ++++++++++++-
.../main/java/resources/query/QueryWidget.html | 7 +++
.../management/plugin/csv/CSVFormatTest.java | 14 ++++++
10 files changed, 151 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/QPID-8066.tar.bz2
----------------------------------------------------------------------
diff --git a/QPID-8066.tar.bz2 b/QPID-8066.tar.bz2
new file mode 100644
index 0000000..1fe1414
Binary files /dev/null and b/QPID-8066.tar.bz2 differ
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/csv/CSVFormat.java
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/csv/CSVFormat.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/csv/CSVFormat.java
index 5636477..e738a82 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/csv/CSVFormat.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/csv/CSVFormat.java
@@ -22,7 +22,11 @@ package org.apache.qpid.server.management.plugin.csv;
import java.io.IOException;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
import java.util.Collection;
+import java.util.Date;
/**
* Simplified version of CSVFormat from Apache Commons CSV
@@ -44,6 +48,7 @@ public final class CSVFormat
private static final char LF = '\n';
private static final char SP = ' ';
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private final char _delimiter;
@@ -120,9 +125,24 @@ public final class CSVFormat
{
charSequence = EMPTY;
}
+ else if (value instanceof CharSequence)
+ {
+ charSequence = (CharSequence) value;
+ }
+ else if (value instanceof Date || value instanceof Calendar)
+ {
+ final Date time = value instanceof Calendar ? ((Calendar) value).getTime() : (Date) value;
+
+ // CSV standard (rfc4180) does not specify the date time format
+ // Excel CSV format is local specific
+ // Some posts on stackoverflow indicate that Excel should support format "yyyy-MM-dd HH:mm:ss".
+ // for example, https://stackoverflow.com/questions/804118/best-timestamp-format-for-csv-excel
+ // Perhaps, it would be better to convert datetime into long (similar to json datetime representation)
+ charSequence = DATE_TIME_FORMATTER.format(time.toInstant().atZone(ZoneId.systemDefault()));
+ }
else
{
- charSequence = value instanceof CharSequence ? (CharSequence) value : value.toString();
+ charSequence = value.toString();
}
this.print(out, value, charSequence, 0, charSequence.length(), newRecord);
}
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
index 4403200..76d87f1 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.management.plugin.servlet.rest;
import static org.apache.qpid.server.management.plugin.HttpManagementUtil.CONTENT_ENCODING_HEADER;
import static org.apache.qpid.server.management.plugin.HttpManagementUtil.GZIP_CONTENT_ENCODING;
+import static org.apache.qpid.server.management.plugin.HttpManagementUtil.ensureFilenameIsRfc2183;
import java.io.IOException;
import java.io.OutputStream;
@@ -66,6 +67,11 @@ import org.apache.qpid.server.util.ConnectionScopedRuntimeException;
public abstract class AbstractServlet extends HttpServlet
{
public static final int SC_UNPROCESSABLE_ENTITY = 422;
+ /**
+ * Signifies that the agent wishes the servlet to set the Content-Disposition on the
+ * response with the value attachment. This filename will be derived from the parameter value.
+ */
+ public static final String CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM = "contentDispositionAttachmentFilename";
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractServlet.class);
public static final String CONTENT_DISPOSITION = "Content-Disposition";
@@ -158,6 +164,23 @@ public abstract class AbstractServlet extends HttpServlet
}
}
+ protected void setContentDispositionHeaderIfNecessary(final HttpServletResponse response,
+ final String attachmentFilename)
+ {
+ if (attachmentFilename != null)
+ {
+ String filenameRfc2183 = ensureFilenameIsRfc2183(attachmentFilename);
+ if (filenameRfc2183.length() > 0)
+ {
+ response.setHeader(CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", filenameRfc2183));
+ }
+ else
+ {
+ response.setHeader(CONTENT_DISPOSITION, "attachment"); // Agent will allow user to choose a name
+ }
+ }
+ }
+
protected void doPut(HttpServletRequest req,
HttpServletResponse resp,
final ConfiguredObject<?> managedObject) throws ServletException, IOException
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueryServlet.java
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueryServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueryServlet.java
index 2b72295..1214a34 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueryServlet.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/QueryServlet.java
@@ -21,6 +21,8 @@
package org.apache.qpid.server.management.plugin.servlet.rest;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -33,6 +35,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.qpid.server.filter.SelectorParsingException;
+import org.apache.qpid.server.management.plugin.csv.CSVFormat;
import org.apache.qpid.server.management.plugin.servlet.query.ConfiguredObjectQuery;
import org.apache.qpid.server.management.plugin.servlet.query.EvaluationException;
import org.apache.qpid.server.model.ConfiguredObject;
@@ -42,6 +45,7 @@ public abstract class QueryServlet<X extends ConfiguredObject<?>> extends Abstra
{
private static final Logger LOGGER = LoggerFactory.getLogger(QueryServlet.class);
+ private static final CSVFormat CSV_FORMAT = new CSVFormat();
@Override
protected void doGet(HttpServletRequest request,
@@ -78,7 +82,6 @@ public abstract class QueryServlet<X extends ConfiguredObject<?>> extends Abstra
if (category != null)
{
List<ConfiguredObject<?>> objects = getAllObjects(parent, category, request);
- Map<String, Object> resultsObject = new LinkedHashMap<>();
try
{
@@ -89,10 +92,26 @@ public abstract class QueryServlet<X extends ConfiguredObject<?>> extends Abstra
request.getParameter("limit"),
request.getParameter("offset"));
- resultsObject.put("headers", query.getHeaders());
- resultsObject.put("results", query.getResults());
- resultsObject.put("total", query.getTotalNumberOfRows());
- sendJsonResponse(resultsObject, request, response);
+
+ String attachmentFilename = request.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM);
+ if (attachmentFilename != null)
+ {
+ setContentDispositionHeaderIfNecessary(response, attachmentFilename);
+ }
+
+ if ("csv".equalsIgnoreCase(request.getParameter("format")))
+ {
+ sendCsvResponse(query, response);
+ }
+ else
+ {
+ Map<String, Object> resultsObject = new LinkedHashMap<>();
+ resultsObject.put("headers", query.getHeaders());
+ resultsObject.put("results", query.getResults());
+ resultsObject.put("total", query.getTotalNumberOfRows());
+
+ sendJsonResponse(resultsObject, request, response);
+ }
}
catch (SelectorParsingException e)
{
@@ -125,6 +144,21 @@ public abstract class QueryServlet<X extends ConfiguredObject<?>> extends Abstra
}
+ private void sendCsvResponse(final ConfiguredObjectQuery query,
+ final HttpServletResponse response)
+ throws IOException
+ {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType("text/csv;charset=utf-8;");
+ response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ sendCachingHeadersOnResponse(response);
+ try (PrintWriter writer = response.getWriter())
+ {
+ CSV_FORMAT.printRecord(writer, query.getHeaders());
+ CSV_FORMAT.printRecords(writer, query.getResults());
+ }
+ }
+
abstract protected X getParent(final HttpServletRequest request, final ConfiguredObject<?> managedObject);
abstract protected Class<? extends ConfiguredObject> getSupportedCategory(final String categoryName,
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
index 8361886..4e5e524 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
@@ -81,11 +81,6 @@ public class RestServlet extends AbstractServlet
public static final String EXTRACT_INITIAL_CONFIG_PARAM = "extractInitialConfig";
public static final String EXCLUDE_INHERITED_CONTEXT_PARAM = "excludeInheritedContext";
private static final String SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST = "singletonModelObjectResponseAsList";
- /**
- * Signifies that the agent wishes the servlet to set the Content-Disposition on the
- * response with the value attachment. This filename will be derived from the parameter value.
- */
- public static final String CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM = "contentDispositionAttachmentFilename";
public static final Set<String> RESERVED_PARAMS =
new HashSet<>(Arrays.asList(DEPTH_PARAM,
SORT_PARAM,
@@ -313,23 +308,6 @@ public class RestServlet extends AbstractServlet
return false;
}
- private void setContentDispositionHeaderIfNecessary(final HttpServletResponse response,
- final String attachmentFilename)
- {
- if (attachmentFilename != null)
- {
- String filenameRfc2183 = ensureFilenameIsRfc2183(attachmentFilename);
- if (filenameRfc2183.length() > 0)
- {
- response.setHeader(CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", filenameRfc2183));
- }
- else
- {
- response.setHeader(CONTENT_DISPOSITION, "attachment"); // Agent will allow user to choose a name
- }
- }
- }
-
private Class<? extends ConfiguredObject> getConfiguredClass(HttpServletRequest request, ConfiguredObject<?> managedObject)
{
final String[] servletPathElements = request.getServletPath().split("/");
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/resources/css/common.css
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/resources/css/common.css b/broker-plugins/management-http/src/main/java/resources/css/common.css
index e4b2511..b2577c7 100644
--- a/broker-plugins/management-http/src/main/java/resources/css/common.css
+++ b/broker-plugins/management-http/src/main/java/resources/css/common.css
@@ -611,6 +611,10 @@ td.advancedSearchField, col.autoWidth {
background-position: -180px -98px;
}
+.exportIcon.ui-icon {
+ background-position: -112px -112px;
+}
+
.claro .searchBox {
padding-right: 16px;
padding-left: 16px;
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Management.js
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Management.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Management.js
index 5570636..bf56bf4 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Management.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Management.js
@@ -612,17 +612,26 @@ define(["dojo/_base/lang",
// Promise returned by dojo.request.xhr with modified then method allowing to use default error handler if none is specified.
Management.prototype.query = function (query)
{
- var url = "api/latest/" + (query.parent && query.parent.type === "virtualhost" ? "queryvhost/"
- + this.objectToPath({parent: query.parent}) : "querybroker") + (query.category ? "/"
- + query.category : "");
var request = {
- url: this.getFullUrl(url),
+ url: this.getQueryUrl(query),
query: {}
};
shallowCopy(query, request.query, ["parent", "category"]);
return this.get(request);
};
+ Management.prototype.getQueryUrl = function (query, parameters)
+ {
+ var url = "api/latest/" + (query.parent && query.parent.type === "virtualhost" ? "queryvhost/"
+ + this.objectToPath({parent: query.parent}) : "querybroker") + (query.category ? "/"
+ + query.category : "");
+ if (parameters)
+ {
+ url = url + "?" + ioQuery.objectToQuery(parameters);
+ }
+ return this.getFullUrl(url);
+ };
+
Management.prototype.savePreference = function(parentObject, preference)
{
var url = this.buildPreferenceUrl(parentObject, preference.type);
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/resources/js/qpid/management/query/QueryWidget.js
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/query/QueryWidget.js b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/query/QueryWidget.js
index 4f6dad6..892e7ff 100644
--- a/broker-plugins/management-http/src/main/java/resources/js/qpid/management/query/QueryWidget.js
+++ b/broker-plugins/management-http/src/main/java/resources/js/qpid/management/query/QueryWidget.js
@@ -35,6 +35,7 @@ define(["dojo/_base/declare",
"dgrid/extensions/ColumnHider",
"qpid/management/query/QueryGrid",
"qpid/common/MessageDialog",
+ "dojox/uuid/generateRandomUuid",
"qpid/management/query/DropDownSelect",
"qpid/management/query/WhereExpression",
"dijit/_WidgetBase",
@@ -61,7 +62,8 @@ define(["dojo/_base/declare",
ColumnReorder,
ColumnHider,
QueryGrid,
- MessageDialog)
+ MessageDialog,
+ uuid)
{
var QueryCloneDialogForm = declare("qpid.management.query.QueryCloneDialogForm",
@@ -183,6 +185,8 @@ define(["dojo/_base/declare",
cloneButtonTooltip: null,
deleteButtonTooltip: null,
searchForm: null,
+ exportButton: null,
+ exportButtonTooltip: null,
/**
* constructor parameter
@@ -241,6 +245,7 @@ define(["dojo/_base/declare",
this.saveButton.on("click", lang.hitch(this, this._saveQuery));
this.cloneButton.on("click", lang.hitch(this, this._cloneQuery));
this.deleteButton.on("click", lang.hitch(this, this._deleteQuery));
+ this.exportButton.on("click", lang.hitch(this, this._exportQueryResults));
this._ownQuery = !this.preference
|| !this.preference.owner
@@ -248,6 +253,7 @@ define(["dojo/_base/declare",
var newQuery = !this.preference || !this.preference.createdDate;
this.saveButton.set("disabled", !this._ownQuery);
this.deleteButton.set("disabled", !this._ownQuery || newQuery);
+ this.exportButton.set("disabled", true);
if (!this._ownQuery)
{
@@ -537,6 +543,7 @@ define(["dojo/_base/declare",
zeroBased: false,
rowsPerPage: rowsPerPage,
_currentPage: currentPage,
+ allowTextSelection: true,
transformer: function (data)
{
var dataResults = data.results;
@@ -585,6 +592,7 @@ define(["dojo/_base/declare",
_queryCompleted: function (e)
{
this._buildColumnsIfHeadersChanged(e.data);
+ this.exportButton.set("disabled", !(e.data.total && e.data.total > 0));
},
_buildColumnsIfHeadersChanged: function (data)
{
@@ -977,6 +985,27 @@ define(["dojo/_base/declare",
this.emit("change", {preference: pref});
}
}
+ },
+ _exportQueryResults: function () {
+ var query = this._getQuery();
+ delete query.category;
+ var params = {};
+ for(var fieldName in query)
+ {
+ if (query.hasOwnProperty(fieldName) && query[fieldName])
+ {
+ params[fieldName] = query[fieldName];
+ }
+ }
+ params.format = "csv";
+ var id = uuid();
+ params.contentDispositionAttachmentFilename ="query-results-" + id + ".csv";
+ var url = this.management.getQueryUrl({category: this.categoryName, parent: this.parentObject}, params);
+ var iframe = document.createElement('iframe');
+ iframe.id = "query_downloader_" + id;
+ iframe.style.display = "none";
+ document.body.appendChild(iframe);
+ iframe.src = url;
}
});
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/main/java/resources/query/QueryWidget.html
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/main/java/resources/query/QueryWidget.html b/broker-plugins/management-http/src/main/java/resources/query/QueryWidget.html
index ff1b471..0a28eac 100644
--- a/broker-plugins/management-http/src/main/java/resources/query/QueryWidget.html
+++ b/broker-plugins/management-http/src/main/java/resources/query/QueryWidget.html
@@ -40,6 +40,13 @@
<div data-dojo-attach-point="deleteButtonTooltip"
data-dojo-type="dijit/Tooltip"
data-dojo-props="connectId:'deleteButton_${id}',position:['below']">Delete query from preferences and close the tab</div>
+ <div id="exportButton_${id}"
+ data-dojo-type="dijit/form/Button"
+ data-dojo-attach-point="exportButton"
+ data-dojo-props="iconClass: 'exportIcon ui-icon'">Export</div>
+ <div data-dojo-attach-point="exportButtonTooltip"
+ data-dojo-type="dijit/Tooltip"
+ data-dojo-props="connectId:'exportButton_${id}',position:['below']">Export query results into CSV format</div>
<div data-dojo-type="dijit/form/Button"
data-dojo-attach-point="modeButton"
data-dojo-props="iconClass: 'advancedViewIcon ui-icon', title:'Switch to \'Advanced View\' search using SQL-like expressions'"
http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/686712e8/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/csv/CSVFormatTest.java
----------------------------------------------------------------------
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/csv/CSVFormatTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/csv/CSVFormatTest.java
index 4e08315..2e68c40 100644
--- a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/csv/CSVFormatTest.java
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/csv/CSVFormatTest.java
@@ -21,7 +21,9 @@
package org.apache.qpid.server.management.plugin.csv;
import java.io.StringWriter;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Date;
import org.apache.qpid.test.utils.QpidTestCase;
@@ -72,6 +74,18 @@ public class CSVFormatTest extends QpidTestCase
out.toString());
}
+ public void testDate() throws Exception
+ {
+ Date date = new Date();
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ CSVFormat csvFormat = new CSVFormat();
+ final StringWriter out = new StringWriter();
+ csvFormat.print(out, date, true);
+ assertEquals("Unexpected format ",
+ simpleDateFormat.format(date),
+ out.toString());
+ }
+
public void testPrintComments() throws Exception
{
CSVFormat csvFormat = new CSVFormat();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org