Skip to content

Commit

Permalink
Validate queries and allow for custom JSON queries. Closes #168. Closes
Browse files Browse the repository at this point in the history
  • Loading branch information
Rashid Khan committed Jul 31, 2014
1 parent af9800b commit 52e873c
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.DS_Store
node_modules
src/bower_components
**/styles/*.css
**/*.css
trash
build
target
18 changes: 13 additions & 5 deletions src/kibana/apps/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
{{dash.title}}
</span>

<form class="fill inline-form" ng-submit="filterResults()">
<input placeholder="Filter..." type="text" class="form-control" ng-model="state.query">
<button class="btn btn-default" type="submit">
<span class="fa fa-search"></span>
</button>
<form class="fill inline-form" ng-submit="filterResults()" name="queryInput">
<div class="input-group"
ng-class="queryInput.$invalid ? 'has-error' : ''">
<input query-input
placeholder="Filter..."
type="text"
class="form-control"
ng-model="state.query">
<button class="btn btn-default" type="submit"
ng-disabled="queryInput.$invalid">
<span class="fa fa-search"></span>
</button>
</div>
</form>

<div class="button-group">
Expand Down
6 changes: 1 addition & 5 deletions src/kibana/apps/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,7 @@ define(function (require) {
function updateQueryOnRootSource() {
if ($state.query) {
dash.searchSource.set('filter', {
query: {
query_string: {
query: $state.query
}
}
query: $state.query
});
} else {
dash.searchSource.set('filter', null);
Expand Down
9 changes: 3 additions & 6 deletions src/kibana/apps/discover/controllers/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ define(function (require) {
require('filters/moment');
require('components/courier/courier');
require('components/index_patterns/index_patterns');
require('components/query_input/query_input');
require('components/state_management/app_state');
require('services/timefilter');

Expand Down Expand Up @@ -70,7 +71,7 @@ define(function (require) {
var defaultFormat = courier.indexPatterns.fieldFormats.defaultByType.string;

var stateDefaults = {
query: initialQuery ? initialQuery.query_string.query : '',
query: initialQuery || '',
columns: ['_source'],
index: config.get('defaultIndex'),
interval: 'auto'
Expand Down Expand Up @@ -291,11 +292,7 @@ define(function (require) {
}
return sort;
})
.query(!$state.query ? null : {
query_string: {
query: $state.query
}
});
.query(!$state.query ? null : $state.query);

// get the current indexPattern
var indexPattern = $scope.searchSource.get('index');
Expand Down
17 changes: 13 additions & 4 deletions src/kibana/apps/discover/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
<div ng-controller="discover">
<navbar>
<form class="fill inline-form" ng-submit="fetch()">
<input placeholder="Search..." type="text" class="form-control" ng-model="state.query">
<button type="submit"><span class="fa fa-search"></span></button>
<button type="button" ng-click="resetQuery()"><span class="fa fa-ban"></span></button>
<form class="fill inline-form" ng-submit="fetch()" name="discoverSearch">
<div class="input-group"
ng-class="discoverSearch.$invalid ? 'has-error' : ''">
<input query-input="searchSource"
ng-model="state.query"
placeholder="Search..."
type="text"
class="form-control">
<button type="submit"
ng-disabled="discoverSearch.$invalid">
<span class="fa fa-search"></span></button>
<button type="button" ng-click="resetQuery()"><span class="fa fa-ban"></span></button>
</div>
</form>

<div class="button-group">
Expand Down
4 changes: 2 additions & 2 deletions src/kibana/apps/visualize/controllers/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ define(function (require) {
delete vis.savedSearchId;

var q = vis.searchSource.get('query');
$state.query = _.isObject(q) ? q.query_string.query : q;
$state.query = q;

var parent = vis.searchSource.parent();
// we will copy over all state minus the "aggs"
Expand All @@ -249,7 +249,7 @@ define(function (require) {
delete $state.query;
} else {
var q = $state.query || vis.searchSource.get('query');
$state.query = _.isObject(q) ? q.query_string.query : q;
$state.query = q;
}

// init
Expand Down
20 changes: 13 additions & 7 deletions src/kibana/apps/visualize/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
<i class="fa fa-chain-broken"></i> Unlinked!
</div>

<form ng-submit="doVisualize()" class="inline-form">
<input
placeholder="Search..."
type="text"
class="form-control"
ng-model="state.query">
<button class="btn btn-default" type="submit"><span class="fa fa-search"></span></button>
<form ng-submit="doVisualize()" class="inline-form" name="queryInput">
<div class="input-group"
ng-class="queryInput.$invalid ? 'has-error' : ''">
<input query-input="vis.searchSource"
placeholder="Search..."
type="text"
class="form-control"
ng-model="state.query">
<button class="btn btn-default" type="submit"
ng-disabled="queryInput.$invalid">
<span class="fa fa-search"></span>
</button>
</div>
</form>
</div>

Expand Down
112 changes: 112 additions & 0 deletions src/kibana/components/query_input/query_input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
define(function (require) {
var _ = require('lodash');
var $ = require('jquery');

require('css!components/query_input/query_input.css');

require('modules')
.get('kibana')
.directive('queryInput', function (es, $compile, timefilter, configFile) {
return {
restrict: 'A',
require: 'ngModel',
scope: {
'ngModel': '=',
'queryInput': '=?',
},
link: function ($scope, elem, attr, ngModel) {

// track request so we can abort it if needed
var request = {};

var errorElem = $('<i class="fa fa-ban input-query-error"></i>').hide();

var init = function () {
elem.after(errorElem);
validater($scope.ngModel);
};

var validater = function (query) {
var index, type;

var error = function (resp) {
ngModel.$setValidity('queryInput', false);

errorElem.attr('tooltip', resp.explanations && resp.explanations[0] ?
resp.explanations[0].error : undefined);

// Compile is needed for the tooltip
$compile(errorElem)($scope);
errorElem.show();

return undefined;
};

var success = function (resp) {
if (resp.valid) {
ngModel.$setValidity('queryInput', true);
errorElem.hide();
return query;
} else {
return error(resp);
}
};

if ($scope.queryInput) {
index = $scope.queryInput.get('index').toIndexList();
} else {
index = configFile.kibanaIndex;
type = '__kibanaQueryValidator';
}

if (request.abort) request.abort();

request = es.indices.validateQuery({
index: index,
type: type,
explain: true,
ignoreUnavailable: true,
body: {
query: query || { match_all: {} }
}
}).then(success, error);
};

var debouncedValidator = _.debounce(validater, 300);


// What should I make with the input from the user?
var fromUser = function (text) {
try {
return JSON.parse(text);
} catch (e) {
return {
query_string: {
query: text || '*'
}
};
}
};

// How should I present the data back to the user in the input field?
var toUser = function (text) {
if (_.isString(text)) return text;
if (_.isObject(text)) {
if (text.query_string) return text.query_string.query;
return JSON.stringify(text);
}
return undefined;
};

ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);

// Use a model watch instead of parser/formatter. Debounced anyway. Parsers require the
// user to actually enter input, which may not happen if the back button is clicked
$scope.$watch('ngModel', debouncedValidator);

init();
}
};
});
});
11 changes: 11 additions & 0 deletions src/kibana/components/query_input/query_input.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import (reference) "../../styles/_bootstrap.less";
@import (reference) "../../styles/theme/_theme.less";
@import (reference) "../../styles/_variables.less";

i.query-input-error {
position: absolute;
margin-left: -25px;
color: @brand-danger;
margin-top: 10px;
z-index: 5;
}
5 changes: 3 additions & 2 deletions src/kibana/styles/_navbar.less
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ navbar {

// horizontal group of buttons/form elements
.button-group,
.inline-form {
.inline-form .input-group {
margin-bottom: 0px;
.display(flex);

> * {
Expand All @@ -90,7 +91,7 @@ navbar {
}
}

.inline-form {
.inline-form .input-group {
input {
height: auto;
}
Expand Down
1 change: 1 addition & 0 deletions tasks/config/less.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var bc = require('path').join(__dirname, '../../src/bower_components');
module.exports = {
src: {
src: [
'<%= src %>/kibana/components/*/*.less',
'<%= src %>/kibana/apps/dashboard/styles/main.less',
'<%= src %>/kibana/apps/discover/styles/main.less',
'<%= src %>/kibana/apps/settings/styles/main.less',
Expand Down

0 comments on commit 52e873c

Please sign in to comment.