Skip to content

Commit

Permalink
UI support for streaming CSV export to the browser
Browse files Browse the repository at this point in the history
requires #1149

issue #765
  • Loading branch information
kroepke committed May 12, 2015
1 parent 93bb86e commit d8e8c61
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 16 deletions.
19 changes: 9 additions & 10 deletions app/controllers/SearchController.java
Expand Up @@ -44,7 +44,6 @@
import org.graylog2.restclient.models.api.responses.QueryParseError;
import org.graylog2.restclient.models.api.results.DateHistogramResult;
import org.graylog2.restclient.models.api.results.SearchResult;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import play.libs.Json;
import play.mvc.Result;
Expand Down Expand Up @@ -253,16 +252,16 @@ public Result exportAsCsv(String q, String filter, String rangeType, int relativ
return status(400, views.html.errors.error.render("Invalid range type provided.", e1, request()));
}

final String s;
try {
final String s = "obsolete";
// try {
Set<String> selectedFields = getSelectedFields(fields);
s = search.searchAsCsv(selectedFields);
} catch (IOException e) {
return status(504, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request()));
} catch (APIException e) {
String message = "There was a problem with your search. We expected HTTP 200, but got a HTTP " + e.getHttpCode() + ".";
return status(504, views.html.errors.error.render(message, e, request()));
}
//s = search.searchAsCsv(selectedFields);
// } catch (IOException e) {
// return status(504, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request()));
// } catch (APIException e) {
// String message = "There was a problem with your search. We expected HTTP 200, but got a HTTP " + e.getHttpCode() + ".";
// return status(504, views.html.errors.error.render(message, e, request()));
// }

// TODO streaming the result
response().setContentType(MediaType.CSV_UTF_8.toString());
Expand Down
8 changes: 4 additions & 4 deletions app/controllers/SearchControllerV2.java
Expand Up @@ -67,6 +67,7 @@
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
Expand Down Expand Up @@ -423,21 +424,20 @@ public Result exportAsCsv(String q,
return status(400, views.html.errors.error.render("Invalid range type provided.", e1, request()));
}

final String s;
final InputStream stream;
try {
Set<String> selectedFields = getSelectedFields(fields);
s = search.searchAsCsv(selectedFields);
stream = search.searchAsCsv(selectedFields);
} catch (IOException e) {
return status(504, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request()));
} catch (APIException e) {
String message = "There was a problem with your search. We expected HTTP 200, but got a HTTP " + e.getHttpCode() + ".";
return status(504, views.html.errors.error.render(message, e, request()));
}

// TODO streaming the result
response().setContentType(MediaType.CSV_UTF_8.toString());
response().setHeader("Content-Disposition", "attachment; filename=graylog-searchresult.csv");
return ok(s);
return ok(stream);
}

protected List<Field> getAllFields() {
Expand Down
2 changes: 1 addition & 1 deletion app/views/helpers/SearchRouteHelper.java
Expand Up @@ -87,7 +87,7 @@ public static Call getCsvRoute(Http.Request request, Stream stream, UniversalSea
TimeRange timeRange = search.getTimeRange();

if (stream == null) {
return routes.SearchController.exportAsCsv(
return routes.SearchControllerV2.exportAsCsv(
query,
"",
timeRange.getType().toString().toLowerCase(),
Expand Down
1 change: 1 addition & 0 deletions app/views/partials/navbar.scala.html
Expand Up @@ -34,6 +34,7 @@
routes.javascript.SearchController.globalSearch,
routes.javascript.SearchControllerV2.index,
routes.javascript.SearchControllerV2.showMessage,
routes.javascript.SearchControllerV2.exportAsCsv,
routes.javascript.SessionsController.destroy,
routes.javascript.StreamsController.index,
routes.javascript.StreamRulesController.index,
Expand Down
2 changes: 1 addition & 1 deletion conf/routes
Expand Up @@ -19,7 +19,7 @@ GET /startpage
# Search
GET /search @controllers.SearchController.index(q ?= "", rangetype ?= "", relative:Integer ?= -1, from ?= "", to ?= "", keyword ?= "", interval ?= "", page:Integer ?= 1, saved:String ?= "", sortField:String ?= "", sortOrder:String ?= "", fields:String ?= "", width:Integer ?= -1)
GET /searchv2 @controllers.SearchControllerV2.index(q ?= "", rangetype ?= "relative", relative:Integer ?= 600, from ?= "", to ?= "", keyword ?= "", interval ?= "", page:Integer ?= 1, saved:String ?= "", sortField:String ?= "", sortOrder:String ?= "", fields:String ?= "", width:Integer ?= -1)
GET /search/csv @controllers.SearchController.exportAsCsv(q ?= "", filter ?= "", rangetype ?= "", relative:Integer ?= -1, from ?= "", to ?= "", keyword ?= "", fields:String ?= "")
GET /searchv2/csv @controllers.SearchControllerV2.exportAsCsv(q ?= "", filter ?= "", rangetype ?= "", relative:Integer ?= -1, from ?= "", to ?= "", keyword ?= "", fields:String ?= "")

# Messages
GET /messages/:index/:id @controllers.SearchControllerV2.showMessage(index: String, id: String)
Expand Down
3 changes: 3 additions & 0 deletions javascript/src/components/search/SearchSidebar.jsx
@@ -1,3 +1,4 @@
/* global jsRoutes */
'use strict';

var React = require('react');
Expand All @@ -8,6 +9,7 @@ var Button = require('react-bootstrap').Button;
var Input = require('react-bootstrap').Input;

var Widget = require('../widgets/Widget');
var SearchStore = require('../../stores/search/SearchStore');
var AddToDashboardMenu = require('../dashboard/AddToDashboardMenu');

var numeral = require('numeral');
Expand Down Expand Up @@ -120,6 +122,7 @@ var SearchSidebar = React.createClass({
dashboards={this.props.dashboards}/>
&nbsp;
<a href="#" className="btn btn-success btn-sm">Save search criteria</a>
<a href={SearchStore.getCsvExportURL()} className="btn btn-default btn-sm">Export as CSV</a>
</div>

<hr />
Expand Down
7 changes: 7 additions & 0 deletions javascript/src/stores/search/SearchStore.ts
Expand Up @@ -238,6 +238,13 @@ class SearchStore {
searchURLParams = searchURLParams.set(param, value);
URLUtils.openLink(jsRoutes.controllers.SearchControllerV2.index().url + "?" + Qs.stringify(searchURLParams.toJS()));
}

getCsvExportURL(): string {
var searchURLParams = this.getOriginalSearchURLParams();
searchURLParams = searchURLParams.delete('page');
// function (q,filter,rangetype,relative,from,to,keyword,fields)
return jsRoutes.controllers.SearchControllerV2.exportAsCsv().url + "?" + Qs.stringify(searchURLParams.toJS());
}
}

var searchStore = new SearchStore();
Expand Down

0 comments on commit d8e8c61

Please sign in to comment.