mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Merge pull request #5208 from BigFunger/parse_query
removed validate-query directive. replaced with parse-query directive
This commit is contained in:
commit
a2dac5d6b4
11 changed files with 41 additions and 242 deletions
|
@ -11,14 +11,15 @@
|
|||
<div class="input-group"
|
||||
ng-class="queryInput.$invalid ? 'has-error' : ''">
|
||||
|
||||
<input type="text"
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter..."
|
||||
aria-label="Filter input"
|
||||
class="form-control"
|
||||
ng-model="state.query"
|
||||
input-focus
|
||||
kbn-typeahead-input
|
||||
validate-query>
|
||||
parse-query>
|
||||
<button type="submit" class="btn btn-default" ng-disabled="queryInput.$invalid" aria-label="Filter dashboards">
|
||||
<span aria-hidden="true" class="fa fa-search"></span>
|
||||
</button>
|
||||
|
|
|
@ -14,7 +14,6 @@ define(function (require) {
|
|||
require('ui/timepicker');
|
||||
require('ui/fixedScroll');
|
||||
require('ui/directives/validate_json');
|
||||
require('ui/validate_query');
|
||||
require('ui/filters/moment');
|
||||
require('ui/courier');
|
||||
require('ui/index_patterns');
|
||||
|
@ -246,8 +245,7 @@ define(function (require) {
|
|||
}()));
|
||||
|
||||
$scope.searchSource.onError(function (err) {
|
||||
console.log(err);
|
||||
notify.error('An error occurred with your request. Reset your inputs and try again.');
|
||||
notify.error(err);
|
||||
}).catch(notify.fatal);
|
||||
|
||||
function initForTime() {
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<div class="typeahead" kbn-typeahead="discover">
|
||||
<div class="input-group"
|
||||
ng-class="discoverSearch.$invalid ? 'has-error' : ''">
|
||||
<input validate-query="searchSource"
|
||||
<input
|
||||
parse-query
|
||||
input-focus
|
||||
kbn-typeahead-input
|
||||
ng-model="state.query"
|
||||
|
@ -16,7 +17,6 @@
|
|||
ng-disabled="discoverSearch.$invalid"
|
||||
aria-label="Search">
|
||||
<span aria-hidden="true" class="fa fa-search"></span></button>
|
||||
<!--<button type="button" ng-click="resetQuery()" aria-label="Reset query"><span aria-hidden="true" class="fa fa-ban"></span></button>-->
|
||||
</div>
|
||||
<kbn-typeahead-items></kbn-typeahead-items>
|
||||
</div>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
ng-class="queryInput.$invalid ? 'has-error' : ''">
|
||||
<input
|
||||
ng-model="state.query"
|
||||
validate-query
|
||||
parse-query
|
||||
input-focus
|
||||
kbn-typeahead-input
|
||||
placeholder="Search..."
|
||||
|
|
|
@ -52,6 +52,7 @@ exports.reload = function () {
|
|||
'ui/index_patterns',
|
||||
'ui/listen',
|
||||
'ui/notify',
|
||||
'ui/parse_query',
|
||||
'ui/persisted_log',
|
||||
'ui/private',
|
||||
'ui/promises',
|
||||
|
@ -67,7 +68,6 @@ exports.reload = function () {
|
|||
'ui/typeahead',
|
||||
'ui/url',
|
||||
'ui/validateDateInterval',
|
||||
'ui/validate_query',
|
||||
'ui/watch_multi'
|
||||
];
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input validate-query
|
||||
<input
|
||||
parse-query
|
||||
ng-model="filter.input.query"
|
||||
type="text"
|
||||
class="form-control"
|
||||
|
|
|
@ -4,7 +4,7 @@ var expect = require('expect.js');
|
|||
var ngMock = require('ngMock');
|
||||
|
||||
// Load the kibana app dependencies.
|
||||
require('ui/validate_query');
|
||||
require('ui/parse_query');
|
||||
|
||||
var $rootScope;
|
||||
var $timeout;
|
||||
|
@ -18,42 +18,15 @@ var $elem;
|
|||
|
||||
var cycleIndex = 0;
|
||||
var mockValidateQuery;
|
||||
var markup = '<input ng-model="mockModel" validate-query="mockQueryInput" input-focus type="text">';
|
||||
var markup = '<input ng-model="mockModel" parse-query input-focus type="text">';
|
||||
var fromUser;
|
||||
var toUser = require('ui/validate_query/lib/to_user');
|
||||
|
||||
|
||||
var validEsResponse = function () {
|
||||
return Promise.resolve({ valid: true });
|
||||
};
|
||||
|
||||
var invalidEsResponse = function () {
|
||||
return Promise.reject({ body: { error: 'mock invalid query' } });
|
||||
};
|
||||
|
||||
var checkClass = function (className) {
|
||||
expect($elem.hasClass(className)).to.be(true);
|
||||
};
|
||||
var toUser = require('ui/parse_query/lib/to_user');
|
||||
|
||||
var init = function () {
|
||||
// Load the application
|
||||
ngMock.module('kibana');
|
||||
|
||||
ngMock.module('kibana', function ($provide) {
|
||||
$provide.service('es', function () {
|
||||
return { indices: { validateQuery: function () {} } };
|
||||
});
|
||||
|
||||
// Super simple config stub
|
||||
$provide.service('config', function () {
|
||||
var keys = {};
|
||||
return {
|
||||
get: function (key) { return keys[key]; },
|
||||
set: function (key, value) { keys[key] = value; }
|
||||
};
|
||||
});
|
||||
|
||||
$provide.constant('kbnIndex', 'test-index');
|
||||
});
|
||||
|
||||
// Create the scope
|
||||
|
@ -66,9 +39,6 @@ var init = function () {
|
|||
|
||||
// Give us a scope
|
||||
$rootScope = _$rootScope_;
|
||||
|
||||
var es = $injector.get('es');
|
||||
mockValidateQuery = sinon.stub(es.indices, 'validateQuery');
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -82,96 +52,22 @@ var compile = function () {
|
|||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
describe('validate-query directive', function () {
|
||||
describe('parse-query directive', function () {
|
||||
describe('initialization', function () {
|
||||
beforeEach(function () {
|
||||
init();
|
||||
mockValidateQuery.returns(validEsResponse());
|
||||
compile();
|
||||
});
|
||||
|
||||
it('should use the model', function () {
|
||||
expect($elemScope).to.have.property('ngModel');
|
||||
});
|
||||
|
||||
it('should call validate with changes', function () {
|
||||
// once for init
|
||||
expect(mockValidateQuery.callCount).to.be(1);
|
||||
|
||||
$rootScope.mockModel = 'different input';
|
||||
$rootScope.$digest();
|
||||
// once on change (leading edge)
|
||||
expect(mockValidateQuery.callCount).to.be(2);
|
||||
$timeout.flush();
|
||||
// once on change (trailing edge)
|
||||
expect(mockValidateQuery.callCount).to.be(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valid querystring', function () {
|
||||
var mockValidateReturns;
|
||||
|
||||
beforeEach(function () {
|
||||
init();
|
||||
mockValidateQuery.returns(validEsResponse());
|
||||
compile();
|
||||
});
|
||||
|
||||
it('should set valid state', function () {
|
||||
// give angular time to set up the directive
|
||||
checkClass('ng-valid-query-input');
|
||||
checkClass('ng-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid querystring', function () {
|
||||
var mockValidateReturns;
|
||||
|
||||
beforeEach(function () {
|
||||
init();
|
||||
mockValidateQuery.returns(invalidEsResponse());
|
||||
compile();
|
||||
});
|
||||
|
||||
it('should set invalid state', function () {
|
||||
checkClass('ng-invalid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('changing input', function () {
|
||||
it('should change validity based on response', function () {
|
||||
init();
|
||||
mockValidateQuery.onCall(0).returns(validEsResponse());
|
||||
compile();
|
||||
$rootScope.$digest();
|
||||
checkClass('ng-valid');
|
||||
|
||||
// leading and trailing edges
|
||||
mockValidateQuery.onCall(1).returns(invalidEsResponse());
|
||||
mockValidateQuery.onCall(2).returns(invalidEsResponse());
|
||||
$rootScope.mockModel = 'invalid:';
|
||||
// trigger model change, which fires watcher
|
||||
$rootScope.$digest();
|
||||
// leading edge
|
||||
$timeout.flush(); // trailing edge
|
||||
checkClass('ng-invalid');
|
||||
|
||||
// leading and trailing edges
|
||||
mockValidateQuery.onCall(3).returns(validEsResponse());
|
||||
mockValidateQuery.onCall(4).returns(validEsResponse());
|
||||
$rootScope.mockModel = 'valid';
|
||||
// trigger model change, which fires watcher
|
||||
$rootScope.$digest();
|
||||
// leading edge
|
||||
$timeout.flush(); // trailing edge
|
||||
checkClass('ng-valid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('user input parser', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
fromUser = Private(require('ui/validate_query/lib/from_user'));
|
||||
fromUser = Private(require('ui/parse_query/lib/from_user'));
|
||||
config.set('query:queryString:options', {});
|
||||
});
|
||||
|
||||
|
@ -193,7 +89,6 @@ describe('validate-query directive', function () {
|
|||
expect(fromUser('')).to.eql({query_string: {query: '*', analyze_wildcard: true}});
|
||||
});
|
||||
|
||||
|
||||
it('should treat input that does not start with { as a query string', function () {
|
||||
expect(fromUser('foo')).to.eql({query_string: {query: 'foo'}});
|
||||
expect(fromUser('400')).to.eql({query_string: {query: '400'}});
|
26
src/ui/public/parse_query/parse_query.js
Normal file
26
src/ui/public/parse_query/parse_query.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
define(function (require) {
|
||||
require('ui/modules')
|
||||
.get('kibana')
|
||||
.directive('parseQuery', function (Private) {
|
||||
var fromUser = Private(require('ui/parse_query/lib/from_user'));
|
||||
var toUser = require('ui/parse_query/lib/to_user');
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
scope: {
|
||||
'ngModel': '='
|
||||
},
|
||||
link: function ($scope, elem, attr, ngModel) {
|
||||
var init = function () {
|
||||
$scope.ngModel = fromUser($scope.ngModel);
|
||||
};
|
||||
|
||||
ngModel.$parsers.push(fromUser);
|
||||
ngModel.$formatters.push(toUser);
|
||||
|
||||
init();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
|
@ -1,122 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
require('ui/debounce');
|
||||
|
||||
require('ui/modules')
|
||||
.get('kibana')
|
||||
.directive('validateQuery', function (es, $compile, timefilter, kbnIndex, debounce, Promise, Private) {
|
||||
var fromUser = Private(require('ui/validate_query/lib/from_user'));
|
||||
var toUser = require('ui/validate_query/lib/to_user');
|
||||
|
||||
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 tooltip={{tooltipMsg}} class="fa fa-ban input-error"></i>').hide();
|
||||
$compile(errorElem)($scope);
|
||||
|
||||
var init = function () {
|
||||
elem.after(errorElem);
|
||||
$scope.ngModel = fromUser($scope.ngModel);
|
||||
validator($scope.ngModel);
|
||||
};
|
||||
|
||||
function validator(query) {
|
||||
if (request.abort) request.abort();
|
||||
|
||||
var prepare = $scope.queryInput ? useSearchSource : useDefaults;
|
||||
|
||||
request = prepare().then(sendRequest);
|
||||
|
||||
function useSearchSource() {
|
||||
var pattern = $scope.queryInput.get('index');
|
||||
|
||||
if (_.isString(pattern)) {
|
||||
return Promise.resolve({ index: pattern });
|
||||
} else if (_.isFunction(_.get(pattern, 'toIndexList'))) {
|
||||
return pattern.toIndexList().then(function (indexList) {
|
||||
return { index: indexList };
|
||||
});
|
||||
} else {
|
||||
return useDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
function useDefaults() {
|
||||
return Promise.resolve({
|
||||
index: kbnIndex,
|
||||
type: '__kibanaQueryValidator'
|
||||
});
|
||||
}
|
||||
|
||||
function sendRequest(config) {
|
||||
return es.indices.validateQuery({
|
||||
index: config.index,
|
||||
type: config.type,
|
||||
explain: true,
|
||||
ignoreUnavailable: true,
|
||||
body: {
|
||||
query: query || { match_all: {} }
|
||||
}
|
||||
})
|
||||
.then(success, error);
|
||||
}
|
||||
|
||||
function error(resp) {
|
||||
var msg;
|
||||
|
||||
ngModel.$setValidity('queryInput', false);
|
||||
|
||||
if (resp.explanations && resp.explanations[0]) {
|
||||
msg = resp.explanations[0].error;
|
||||
} else {
|
||||
msg = resp.body.error;
|
||||
}
|
||||
|
||||
$scope.tooltipMsg = msg;
|
||||
errorElem.show();
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function success(resp) {
|
||||
if (resp.valid) {
|
||||
ngModel.$setValidity('queryInput', true);
|
||||
errorElem.hide();
|
||||
return query;
|
||||
} else {
|
||||
return error(resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var debouncedValidator = debounce(validator, 300, {
|
||||
leading: true,
|
||||
trailing: true
|
||||
});
|
||||
|
||||
ngModel.$parsers.push(fromUser);
|
||||
ngModel.$formatters.push(toUser);
|
||||
|
||||
// Use a model watch instead of parser/formatter. Parsers require the
|
||||
// user to actually enter input, which may not happen if the back button is clicked
|
||||
$scope.$watch('ngModel', function (newValue, oldValue) {
|
||||
if (newValue === oldValue) return;
|
||||
debouncedValidator(newValue);
|
||||
});
|
||||
|
||||
init();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue