Repository: qpid-broker-j Updated Branches: refs/heads/master 51472013e -> 7dbb88471
QPID-8103: [Broker-J] [WMC] [Query UI] Add ability to download results as CSV 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/7dbb8847 Tree: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/tree/7dbb8847 Diff: http://git-wip-us.apache.org/repos/asf/qpid-broker-j/diff/7dbb8847 Branch: refs/heads/master Commit: 7dbb88471a414d993e4ee3bd3e8fde2f30ca2f46 Parents: 5147201 Author: Alex Rudyy <[email protected]> Authored: Sat Feb 24 02:23:26 2018 +0000 Committer: Alex Rudyy <[email protected]> Committed: Sat Feb 24 17:57:45 2018 +0000 ---------------------------------------------------------------------- .../plugin/servlet/csv/CSVFormat.java | 320 +++++++++++++++++++ .../plugin/servlet/rest/AbstractServlet.java | 23 ++ .../plugin/servlet/rest/QueryServlet.java | 58 +++- .../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 | 23 +- .../main/java/resources/query/QueryWidget.html | 7 + .../plugin/servlet/csv/CSVFormatTest.java | 84 +++++ 9 files changed, 526 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/7dbb8847/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormat.java ---------------------------------------------------------------------- diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormat.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormat.java new file mode 100644 index 0000000..90a5f5f --- /dev/null +++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormat.java @@ -0,0 +1,320 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.management.plugin.servlet.csv; + + +import java.io.IOException; +import java.util.Collection; + +/** + * Simplified version of CSVFormat from Apache Commons CSV + */ +public final class CSVFormat +{ + private static final char COMMA = ','; + + private static final char COMMENT = '#'; + + private static final char CR = '\r'; + + private static final String CRLF = "\r\n"; + + private static final Character DOUBLE_QUOTE_CHAR = '"'; + + private static final String EMPTY = ""; + + private static final char LF = '\n'; + + private static final char SP = ' '; + + private final char _delimiter; + + private final Character _escapeCharacter; // null if escaping is disabled + + private final Character _quoteCharacter; // null if quoting is disabled + + private final String _recordSeparator; // for outputs + + public CSVFormat() + { + this(COMMA, DOUBLE_QUOTE_CHAR, null, CRLF); + } + + /** + * Creates a customized CSV format. + * + * @param delimiter the char used for value separation, must not be a line break character + * @param quoteCharacter the Character used as value encapsulation marker, may be {@code null} to disable + * @param escapeCharacter the Character used to escape special characters in values, may be {@code null} to disable + * @param recordSeparator the line separator to use for output + * @throws IllegalArgumentException if the _delimiter is a line break character + */ + CSVFormat(final char delimiter, + final Character quoteCharacter, + final Character escapeCharacter, + final String recordSeparator) + { + if (delimiter == LF || delimiter == CR) + { + throw new IllegalArgumentException("The _delimiter cannot be a line break"); + } + + if (quoteCharacter != null && delimiter == quoteCharacter) + { + throw new IllegalArgumentException( + "The quote character and the delimiter cannot be the same ('" + quoteCharacter + "')"); + } + + if (escapeCharacter != null && delimiter == escapeCharacter) + { + throw new IllegalArgumentException( + "The escape character and the delimiter cannot be the same ('" + escapeCharacter + "')"); + } + + this._delimiter = delimiter; + this._quoteCharacter = quoteCharacter; + this._escapeCharacter = escapeCharacter; + this._recordSeparator = recordSeparator; + } + + public <T extends Collection<?>> void printRecord(final Appendable out, final T record) throws IOException + { + boolean newRecord = true; + for (Object item : record) + { + print(out, item, newRecord); + newRecord = false; + } + println(out); + } + + public <C extends Collection<? extends Collection<?>>> void printRecords(final Appendable out, final C records) + throws IOException + { + for (Collection<?> record : records) + { + printRecord(out, record); + } + } + + + public void println(final Appendable out) throws IOException + { + if (_recordSeparator != null) + { + out.append(_recordSeparator); + } + } + + public void print(final Appendable out, final Object value, final boolean newRecord) throws IOException + { + CharSequence charSequence; + if (value == null) + { + charSequence = EMPTY; + } + else + { + charSequence = value instanceof CharSequence ? (CharSequence) value : value.toString(); + } + this.print(out, value, charSequence, 0, charSequence.length(), newRecord); + } + + + public void printComments(final Appendable out, + final String... comments) throws IOException + { + for (String comment: comments) + { + out.append(COMMENT).append(SP).append(comment); + println(out); + } + } + + private void print(final Appendable out, + final Object object, + final CharSequence value, + final int offset, + final int len, + final boolean newRecord) throws IOException + { + if (!newRecord) + { + out.append(_delimiter); + } + if (object == null) + { + out.append(value); + } + else if (_quoteCharacter != null) + { + printAndQuote(value, offset, len, out, newRecord); + } + else if (_escapeCharacter != null) + { + printAndEscape(out, value, offset, len); + } + else + { + out.append(value, offset, offset + len); + } + } + + private void printAndEscape(final Appendable out, + final CharSequence value, + final int offset, + final int len) + throws IOException + { + int start = offset; + int pos = offset; + final int end = offset + len; + + final char escape = _escapeCharacter; + + while (pos < end) + { + char c = value.charAt(pos); + if (c == CR || c == LF || c == _delimiter || c == escape) + { + // write out segment up until this char + if (pos > start) + { + out.append(value, start, pos); + } + if (c == LF) + { + c = 'n'; + } + else if (c == CR) + { + c = 'r'; + } + + out.append(escape); + out.append(c); + + start = pos + 1; // start on the current char after this one + } + + pos++; + } + + // write last segment + if (pos > start) + { + out.append(value, start, pos); + } + } + + private void printAndQuote(final CharSequence value, final int offset, final int len, + final Appendable out, final boolean newRecord) throws IOException + { + boolean quote = false; + int start = offset; + int pos = offset; + final int end = offset + len; + + final char quoteChar = _quoteCharacter; + + if (len <= 0) + { + // always quote an empty token that is the first + // on the line, as it may be the only thing on the + // line. If it were not quoted in that case, + // an empty line has no tokens. + if (newRecord) + { + quote = true; + } + } + else + { + char c = value.charAt(pos); + + if (c <= COMMENT) + { + // Some other chars at the start of a value caused the parser to fail, so for now + // encapsulate if we start in anything less than '#'. We are being conservative + // by including the default comment char too. + quote = true; + } + else + { + while (pos < end) + { + c = value.charAt(pos); + if (c == LF || c == CR || c == quoteChar || c == _delimiter) + { + quote = true; + break; + } + pos++; + } + + if (!quote) + { + pos = end - 1; + c = value.charAt(pos); + // Some other chars at the end caused the parser to fail, so for now + // encapsulate if we end in anything less than ' ' + if (c <= SP) + { + quote = true; + } + } + } + } + + if (!quote) + { + // no encapsulation needed - write out the original value + out.append(value, start, end); + return; + } + + // we hit something that needed encapsulation + out.append(quoteChar); + + // Pick up where we left off: pos should be positioned on the first character that caused + // the need for encapsulation. + while (pos < end) + { + final char c = value.charAt(pos); + if (c == quoteChar) + { + // write out the chunk up until this point + + // add 1 to the length to write out the encapsulator also + out.append(value, start, pos + 1); + // put the next starting position on the encapsulator so we will + // write it out again with the next string (effectively doubling it) + start = pos; + } + pos++; + } + + // write the last segment + out.append(value, start, pos); + out.append(quoteChar); + } + +} http://git-wip-us.apache.org/repos/asf/qpid-broker-j/blob/7dbb8847/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/7dbb8847/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..8ae06f8 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,15 +35,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.qpid.server.filter.SelectorParsingException; +import org.apache.qpid.server.management.plugin.servlet.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; +import org.apache.qpid.server.model.Container; import org.apache.qpid.server.model.Model; public abstract class QueryServlet<X extends ConfiguredObject<?>> extends AbstractServlet { 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 +83,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 +93,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(categoryName, parent, query, request, 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 +145,34 @@ public abstract class QueryServlet<X extends ConfiguredObject<?>> extends Abstra } + private void sendCsvResponse(final String categoryName, + final X parent, + final ConfiguredObjectQuery query, + final HttpServletRequest request, + 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.printComments(writer, + String.format("parent : %s %s ", + parent.getCategoryClass().getSimpleName(), + (parent instanceof Container + ? "" + : parent.getName())), + String.format("category : %s", categoryName), + String.format("select : %s", request.getParameter("select")), + String.format("where : %s", request.getParameter("where")), + String.format("order by : %s", request.getParameter("orderBy"))); + 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/7dbb8847/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/7dbb8847/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/7dbb8847/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/7dbb8847/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..799ec67 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,19 @@ define(["dojo/_base/declare", this.emit("change", {preference: pref}); } } + }, + _exportQueryResults: function () { + var query = this._getQuery(); + query.format = "csv"; + var id = uuid(); + query.contentDispositionAttachmentFilename ="query-results-" + id + ".csv"; + delete query.category; + var url = this.management.getQueryUrl({category: this.categoryName, parent: this.parentObject}, query); + 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/7dbb8847/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/7dbb8847/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormatTest.java ---------------------------------------------------------------------- diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormatTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormatTest.java new file mode 100644 index 0000000..4adb0b7 --- /dev/null +++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/csv/CSVFormatTest.java @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.management.plugin.servlet.csv; + +import java.io.StringWriter; +import java.util.Arrays; + +import org.apache.qpid.test.utils.QpidTestCase; + +public class CSVFormatTest extends QpidTestCase +{ + + public void testPrintRecord() throws Exception + { + CSVFormat csvFormat = new CSVFormat(); + final StringWriter out = new StringWriter(); + csvFormat.printRecord(out, Arrays.asList("test", 1, true, "\"quoted\" test")); + assertEquals("Unexpected format", + String.format("%s,%d,%b,%s%s", "test", 1, true, "\"\"\"quoted\"\" test\"", "\r\n"), + out.toString()); + } + + public void testPrintRecords() throws Exception + { + CSVFormat csvFormat = new CSVFormat(); + final StringWriter out = new StringWriter(); + csvFormat.printRecords(out, Arrays.asList(Arrays.asList("test", 1, true, "\"quoted\" test"), + Arrays.asList("delimeter,test", 1.0f, false, + "quote\" in the middle"))); + assertEquals("Unexpected format", + String.format("%s,%d,%b,%s%s%s,%s,%b,%s%s", "test", 1, true, "\"\"\"quoted\"\" test\"", "\r\n", + "\"delimeter,test\"", "1.0", false, "\"quote\"\" in the middle\"", "\r\n"), + out.toString()); + } + + public void testPrintln() throws Exception + { + CSVFormat csvFormat = new CSVFormat(); + final StringWriter out = new StringWriter(); + csvFormat.println(out); + assertEquals("Unexpected new line", "\r\n", out.toString()); + } + + public void testPrint() throws Exception + { + CSVFormat csvFormat = new CSVFormat(); + final StringWriter out = new StringWriter(); + csvFormat.print(out, "test", true); + csvFormat.print(out, 1, false); + csvFormat.print(out, true, false); + csvFormat.print(out, "\"quoted\" test", false); + assertEquals("Unexpected format ", + String.format("%s,%d,%b,%s", "test", 1, true, "\"\"\"quoted\"\" test\""), + out.toString()); + } + + public void testPrintComments() throws Exception + { + CSVFormat csvFormat = new CSVFormat(); + final StringWriter out = new StringWriter(); + csvFormat.printComments(out, "comment1", "comment2"); + assertEquals("Unexpected format of comments", + String.format("# %s%s# %s%s", "comment1", "\r\n", "comment2", "\r\n"), + out.toString()); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
