mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
switched to indexPatters as saved objects
This commit is contained in:
parent
554e224d5c
commit
30851d68c9
56 changed files with 888 additions and 953 deletions
|
@ -1,21 +1,17 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('app/dashboard', [
|
||||
'kibana/saved_object'
|
||||
]);
|
||||
var module = require('modules').get('app/dashboard');
|
||||
var _ = require('lodash');
|
||||
var inherits = require('utils/inherits');
|
||||
|
||||
require('saved_object/saved_object');
|
||||
|
||||
// Used only by the savedDashboards service, usually no reason to change this
|
||||
module.factory('SavedDashboard', function (configFile, courier, Promise, createNotifier, SavedObject) {
|
||||
module.factory('SavedDashboard', function (courier) {
|
||||
|
||||
// SavedDashboard constructor. Usually you'd interact with an instance of this.
|
||||
// ID is option, without it one will be generated on save.
|
||||
function SavedDashboard(id) {
|
||||
|
||||
// Gives our SavedDashboard the properties of a SavedObject
|
||||
SavedObject.call(this, {
|
||||
courier.SavedObject.call(this, {
|
||||
// this object will be saved at {{configFile.kibanaIndex}}/dashboard/{{id}}
|
||||
type: 'dashboard',
|
||||
|
||||
|
@ -41,7 +37,7 @@ define(function (require) {
|
|||
}
|
||||
|
||||
// Sets savedDashboard.prototype to an instance of SavedObject
|
||||
inherits(SavedDashboard, SavedObject);
|
||||
inherits(SavedDashboard, courier.SavedObject);
|
||||
|
||||
return SavedDashboard;
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ define(function (require) {
|
|||
require('directives/timepicker');
|
||||
require('directives/fixed_scroll');
|
||||
require('filters/moment');
|
||||
require('apps/settings/services/index_patterns');
|
||||
require('courier/courier');
|
||||
require('state_management/app_state');
|
||||
require('services/timefilter');
|
||||
|
||||
|
@ -40,8 +40,8 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
},
|
||||
indexPatternList: function (indexPatterns) {
|
||||
return indexPatterns.getIds();
|
||||
indexPatternList: function (courier) {
|
||||
return courier.indexPatterns.getIds();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -261,15 +261,15 @@ define(function (require) {
|
|||
}
|
||||
|
||||
function setFields() {
|
||||
return searchSource.getFields($scope.opts.index)
|
||||
.then(function (fields) {
|
||||
return courier.getFieldsFor($scope.opts.index)
|
||||
.then(function (rawFields) {
|
||||
var currentState = _.transform($scope.fields || [], function (current, field) {
|
||||
current[field.name] = {
|
||||
display: field.display
|
||||
};
|
||||
}, {});
|
||||
|
||||
if (!fields) return;
|
||||
if (!rawFields) return;
|
||||
|
||||
var columnObjects = arrayToKeys($scope.state.columns);
|
||||
|
||||
|
@ -279,16 +279,10 @@ define(function (require) {
|
|||
// Inject source into list;
|
||||
$scope.fields.push({name: '_source', type: 'source', display: false});
|
||||
|
||||
_(fields)
|
||||
.keys()
|
||||
.sort()
|
||||
.each(function (name) {
|
||||
var field = fields[name];
|
||||
field.name = name;
|
||||
|
||||
_.defaults(field, currentState[name]);
|
||||
$scope.fields.push(_.defaults(field, {display: columnObjects[name] || false}));
|
||||
});
|
||||
_.sortBy(rawFields, 'name').forEach(function (field) {
|
||||
_.defaults(field, currentState[field.name]);
|
||||
$scope.fields.push(_.defaults(field, {display: columnObjects[name] || false}));
|
||||
});
|
||||
|
||||
// TODO: timefield should be associated with the index pattern, this is a hack
|
||||
// to pick the first date field and use it.
|
||||
|
|
|
@ -9,9 +9,9 @@ define(function (require) {
|
|||
'kibana/courier'
|
||||
]);
|
||||
|
||||
module.factory('SavedSearch', function (configFile, courier, Promise, SavedObject) {
|
||||
module.factory('SavedSearch', function (configFile, courier, Promise) {
|
||||
function SavedSearch(id) {
|
||||
SavedObject.call(this, {
|
||||
courier.SavedObject.call(this, {
|
||||
type: 'search',
|
||||
|
||||
id: id,
|
||||
|
@ -28,10 +28,17 @@ define(function (require) {
|
|||
hits: 0
|
||||
},
|
||||
|
||||
searchSource: true
|
||||
searchSource: true,
|
||||
|
||||
afterESResp: function () {
|
||||
var index = this.searchSource.get('index');
|
||||
if (typeof index === 'string') {
|
||||
this.searchSource.index(courier.indexPatterns.get(index));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
inherits(SavedSearch, SavedObject);
|
||||
inherits(SavedSearch, courier.SavedObject);
|
||||
return SavedSearch;
|
||||
});
|
||||
});
|
|
@ -5,11 +5,10 @@ define(function (require) {
|
|||
require('notify/notify');
|
||||
|
||||
var module = require('modules').get('discover/saved_searches', [
|
||||
'kibana/notify',
|
||||
'kibana/courier'
|
||||
'kibana/notify'
|
||||
]);
|
||||
|
||||
module.service('savedSearches', function (courier, configFile, es, createNotifier, SavedSearch) {
|
||||
module.service('savedSearches', function (configFile, es, createNotifier, SavedSearch) {
|
||||
var notify = createNotifier({
|
||||
location: 'Saved Searches'
|
||||
});
|
||||
|
|
|
@ -3,6 +3,19 @@ define(function (require) {
|
|||
var _ = require('lodash');
|
||||
var configDefaults = require('config/defaults');
|
||||
|
||||
require('../sections').push({
|
||||
order: 2,
|
||||
name: 'advanced',
|
||||
display: 'Advanced',
|
||||
url: '#/settings/advanced',
|
||||
template: require('text!../partials/advanced.html'),
|
||||
resolve: {
|
||||
noId: function ($route, $location) {
|
||||
if ($route.current.params.id) $location.url('/settings/advanced');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.controller('advancedSettings', function ($scope, config, Notifier) {
|
||||
var notify = new Notifier();
|
||||
var configVals = config._vals();
|
||||
|
|
|
@ -1,102 +1,100 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('settings/controllers');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.controller('indexSettings', function ($scope, courier, Notifier, $route, $location, es, config, indexPatterns) {
|
||||
require('courier/courier');
|
||||
|
||||
require('../sections').push({
|
||||
order: 1,
|
||||
name: 'indices',
|
||||
display: 'Indices',
|
||||
url: '#/settings/indices',
|
||||
template: require('text!../partials/indices.html'),
|
||||
resolve: {
|
||||
indexPattern: function ($route, courier) {
|
||||
return courier.indexPatterns.get($route.current.params.id);
|
||||
},
|
||||
indexPatternIds: function (courier) {
|
||||
return courier.indexPatterns.getIds();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.controller('indexSettings', function ($scope, courier, Notifier, $route, $location, es, config) {
|
||||
var notify = new Notifier({
|
||||
location: 'Index Settings'
|
||||
});
|
||||
|
||||
var init = function () {
|
||||
$scope.getPatterns();
|
||||
$scope.indices = {
|
||||
id: $route.current.params.id,
|
||||
table: {
|
||||
by: 'field',
|
||||
reverse: false,
|
||||
page: 0,
|
||||
max: 20
|
||||
},
|
||||
default: config.get('defaultIndex')
|
||||
};
|
||||
$scope.indexPattern = $route.current.locals.indexPattern;
|
||||
$scope.indexPatternIds = $route.current.locals.indexPatternIds;
|
||||
|
||||
if (!!$scope.indices.id) {
|
||||
loadPattern($scope.indices.id);
|
||||
}
|
||||
$scope.defaultIndex = config.get('defaultIndex');
|
||||
$scope.$on('change:config.defaultIndex', function () {
|
||||
$scope.defaultIndex = config.get('defaultIndex');
|
||||
});
|
||||
|
||||
$scope.table = {
|
||||
by: 'name',
|
||||
reverse: false,
|
||||
page: 0,
|
||||
max: 20
|
||||
};
|
||||
|
||||
var loadPattern = function (pattern) {
|
||||
return indexPatterns.getFields(pattern)
|
||||
.then(function (fields) {
|
||||
$scope.indices.mapping = fields;
|
||||
$scope.refreshFields = function () {
|
||||
$scope.indexPattern.refreshFields();
|
||||
};
|
||||
|
||||
$scope.removePattern = function () {
|
||||
courier.indexPatterns.delete($scope.indexPattern)
|
||||
.then(refreshKibanaIndex)
|
||||
.then(function () {
|
||||
$location.path('/settings/indices');
|
||||
})
|
||||
.catch(function () {
|
||||
$location.path('/settings/indices');
|
||||
});
|
||||
.catch(notify.fatal);
|
||||
};
|
||||
|
||||
// TODO: Resolve this in the routes, otherwise the warning will show for a moment;
|
||||
$scope.getPatterns = function (pattern) {
|
||||
return indexPatterns.getIds()
|
||||
.then(function (ids) {
|
||||
$scope.indices.patterns = ids;
|
||||
});
|
||||
};
|
||||
$scope.create = function () {
|
||||
$scope.indexPattern.id = $scope.indexPattern.title;
|
||||
|
||||
$scope.refreshFields = function (pattern) {
|
||||
return indexPatterns.delete(pattern)
|
||||
// refresh the fields, to verify that the
|
||||
$scope.indexPattern.refreshFields()
|
||||
.then(refreshKibanaIndex)
|
||||
.then(function () {
|
||||
return indexPatterns.create(pattern);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removePattern = function (pattern) {
|
||||
indexPatterns.delete(pattern)
|
||||
.then(function () {
|
||||
$location.path('/settings/indices');
|
||||
$location.path('/settings/indices/' + $scope.indexPattern.id);
|
||||
})
|
||||
.catch(function (err) {
|
||||
$location.path('/settings/indices');
|
||||
if (err.status >= 400) notify.error('Could not locate any indices matching that pattern. Please add the index to Elasticsearch');
|
||||
else notify.fatal(err);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addPattern = function (pattern) {
|
||||
indexPatterns.create(pattern)
|
||||
.then(function () {
|
||||
$location.path('/settings/indices/' + pattern);
|
||||
})
|
||||
.catch(function (err) {
|
||||
if (err.status >= 400) {
|
||||
notify.error('Could not locate any indices matching that pattern. Please add the index to Elasticsearch');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setDefaultPattern = function (pattern) {
|
||||
config.set('defaultIndex', pattern)
|
||||
.then(function () {
|
||||
$scope.indices.default = pattern;
|
||||
});
|
||||
$scope.setDefaultPattern = function () {
|
||||
config.set('defaultIndex', $scope.indexPattern.id);
|
||||
};
|
||||
|
||||
$scope.setFieldSort = function (by) {
|
||||
if ($scope.indices.table.by === by) {
|
||||
$scope.indices.table.reverse = !$scope.indices.table.reverse;
|
||||
if ($scope.table.by === by) {
|
||||
$scope.table.reverse = !$scope.table.reverse;
|
||||
} else {
|
||||
$scope.indices.table.by = by;
|
||||
$scope.table.by = by;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.sortClass = function (column) {
|
||||
if ($scope.indices.table.by !== column) return;
|
||||
return $scope.indices.table.reverse ? ['fa', 'fa-sort-asc'] : ['fa', 'fa-sort-desc'];
|
||||
if ($scope.table.by !== column) return;
|
||||
return $scope.table.reverse ? ['fa', 'fa-sort-asc'] : ['fa', 'fa-sort-desc'];
|
||||
};
|
||||
|
||||
$scope.tablePages = function () {
|
||||
if (!$scope.indices.mapping) return 0;
|
||||
return Math.ceil($scope.indices.mapping.length / $scope.indices.table.max);
|
||||
if (!$scope.indexPattern.fields) return 0;
|
||||
return Math.ceil($scope.indexPattern.fields.length / $scope.table.max);
|
||||
};
|
||||
|
||||
init();
|
||||
var refreshKibanaIndex = function () {
|
||||
return es.indices.refresh({
|
||||
index: config.file.kibanaIndex
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$emit('application.load');
|
||||
});
|
||||
|
|
|
@ -6,7 +6,10 @@ define(function (require, module, exports) {
|
|||
require('css!./styles/main.css');
|
||||
|
||||
require('filters/start_from');
|
||||
require('./services/index_patterns');
|
||||
|
||||
var sections = require('./sections');
|
||||
|
||||
// each controller will write it's section config to the sections module
|
||||
require('./controllers/advanced');
|
||||
require('./controllers/indices');
|
||||
|
||||
|
@ -16,31 +19,28 @@ define(function (require, module, exports) {
|
|||
})
|
||||
.when('/settings/:section/:id?', {
|
||||
template: require('text!./index.html'),
|
||||
reloadOnSearch: false
|
||||
});
|
||||
reloadOnSearch: false,
|
||||
resolve: {
|
||||
sectionLocals: function ($route, $location, $injector, Promise) {
|
||||
var section = _.find(sections, { name: $route.current.params.section });
|
||||
if (!section) return $location.url('/settings/');
|
||||
|
||||
var sections = [
|
||||
{
|
||||
name: 'indices',
|
||||
display: 'Indices',
|
||||
url: '#/settings/indices',
|
||||
template: require('text!./partials/indices.html')
|
||||
},
|
||||
{
|
||||
name: 'advanced',
|
||||
display: 'Advanced',
|
||||
url: '#/settings/advanced',
|
||||
template: require('text!./partials/advanced.html')
|
||||
return Promise.props(_.mapValues(section.resolve || {}, function (fn) {
|
||||
return $injector.invoke(fn);
|
||||
}));
|
||||
}
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
app.directive('settingsApp', function ($route, $location, $compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function ($scope, $el) {
|
||||
$scope.sections = sections;
|
||||
$scope.sections = _.sortBy(sections, 'order');
|
||||
$scope.section = _.find($scope.sections, { name: $route.current.params.section });
|
||||
if (!$scope.section) return $location.url('/settings');
|
||||
|
||||
// reassign the sectionLocals to the locals object, so that the section's controller can use their own logic
|
||||
_.unwrapProp($route.current.locals, 'sectionLocals');
|
||||
|
||||
$scope.sections.forEach(function (section) {
|
||||
section.class = (section === $scope.section) ? 'active' : void 0;
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<div class="col-md-2 sidebar-container">
|
||||
<div class="sidebar-list">
|
||||
<div class="sidebar-list-header">
|
||||
<h5>Index Patterns <a ng-show="indices.id" href="#/settings/indices" class="btn btn-primary btn-xs"><i class="fa fa-plus"></i> Add New</a></h5>
|
||||
<h5>Index Patterns <a ng-show="indexPattern.id" href="#/settings/indices" class="btn btn-primary btn-xs"><i class="fa fa-plus"></i> Add New</a></h5>
|
||||
</div>
|
||||
<ul class="list-unstyled">
|
||||
<li class="sidebar-item" ng-repeat="pattern in defaultPattern = (indices.patterns | filter:indices.default)">
|
||||
<li class="sidebar-item" ng-repeat="pattern in defaultPattern = (indexPatternIds | filter:defaultIndex)">
|
||||
<a ng-href="#/settings/indices/{{pattern}}">
|
||||
<div class="sidebar-item-title" ng-class="{'active': pattern == indices.id}">
|
||||
<div class="sidebar-item-title" ng-class="{'active': pattern == indexPattern.id}">
|
||||
<i class="fa fa-star"></i> <span bo-text="pattern"></span>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -17,9 +17,9 @@
|
|||
<strong>Warning:</strong> No default index pattern. You must select or create one to continue.
|
||||
</div>
|
||||
</li>
|
||||
<li class="sidebar-item" ng-repeat="pattern in indices.patterns | filter:'!'+indices.default">
|
||||
<li class="sidebar-item" ng-repeat="pattern in indexPatternIds | filter:'!'+defaultIndex">
|
||||
<a ng-href="#/settings/indices/{{pattern}}">
|
||||
<div class="sidebar-item-title" ng-class="{'active': pattern == indices.id}">
|
||||
<div class="sidebar-item-title" ng-class="{'active': pattern == indexPattern.id}">
|
||||
<span bo-text="pattern"></span>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div ng-show="!indices.id">
|
||||
<div ng-if="!indexPattern.id">
|
||||
<div class="col-md-12">
|
||||
<div class="page-header">
|
||||
<h1>Configure an index pattern</h1>
|
||||
|
@ -39,11 +39,11 @@
|
|||
used to identify the Elasticsearch index to run search and analytics against. They are also
|
||||
used to configure fields.
|
||||
</div>
|
||||
<form name="form" role="form" class="well" ng-submit="addPattern(indices.newIndex.pattern)">
|
||||
<form name="form" role="form" class="well" ng-submit="create()">
|
||||
<div class="form-group">
|
||||
<label>Index Name <small>or pattern</small></label>
|
||||
<input type="text" class="form-control" placeholder="Enter index name"
|
||||
ng-model="indices.newIndex.pattern" required>
|
||||
ng-model="indexPattern.title" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-disabled="form.$invalid">Create</button>
|
||||
|
@ -51,33 +51,43 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="indices.id">
|
||||
<div ng-if="indexPattern.id">
|
||||
<div class="col-md-12">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{indices.id}}
|
||||
<button class="btn btn-success" tooltip="Set as default index" ng-click="setDefaultPattern(indices.id)">
|
||||
{{indexPattern.id}}
|
||||
<button
|
||||
ng-click="setDefaultPattern()"
|
||||
tooltip="Set as default index"
|
||||
class="btn btn-success"
|
||||
ng-disabled="indexPattern.id === defaultIndex">
|
||||
<i class="fa fa-star"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning" tooltip="Refresh field list" ng-click="refreshFields(indices.id)">
|
||||
<button
|
||||
ng-click="indexPattern.refreshFields()"
|
||||
tooltip="Refresh field list"
|
||||
class="btn btn-warning">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger" tooltip="Remove index pattern" ng-click="removePattern(indices.id)">
|
||||
<button
|
||||
ng-click="removePattern()"
|
||||
tooltip="Remove index pattern"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-ban"></i>
|
||||
</button>
|
||||
|
||||
</h1>
|
||||
This page lists every field in the <strong>{{indices.id}}</strong> index and the field's
|
||||
This page lists every field in the <strong>{{indexPattern.id}}</strong> index and the field's
|
||||
associated core type as recorded by Elasticsearch
|
||||
</div>
|
||||
|
||||
<div ng-hide="tablePages() == 1">
|
||||
<center>
|
||||
<button class="btn btn-sm" ng-disabled="indices.table.page == 0" ng-click="indices.table.page=indices.table.page-1">
|
||||
<button class="btn btn-sm" ng-disabled="table.page == 0" ng-click="table.page=table.page-1">
|
||||
Previous
|
||||
</button>
|
||||
{{indices.table.page+1}}/{{tablePages()}}
|
||||
<button class="btn btn-sm" ng-disabled="indices.table.page >= indices.mapping.length/indices.table.max - 1" ng-click="indices.table.page=indices.table.page+1">
|
||||
{{table.page+1}}/{{tablePages()}}
|
||||
<button class="btn btn-sm" ng-disabled="table.page >= indexPattern.fields.length/table.max - 1" ng-click="table.page=table.page+1">
|
||||
Next
|
||||
</button>
|
||||
</center>
|
||||
|
@ -85,12 +95,12 @@
|
|||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th ng-click="setFieldSort('field')">field <i ng-class="sortClass('field')"></i></th>
|
||||
<th ng-click="setFieldSort('mapping.type')">type <i ng-class="sortClass('mapping.type')"></i></th>
|
||||
<th ng-click="setFieldSort('name')">name <i ng-class="sortClass('name')"></i></th>
|
||||
<th ng-click="setFieldSort('type')">type <i ng-class="sortClass('type')"></i></th>
|
||||
</thead>
|
||||
<tr ng-repeat="field in indices.mapping | orderBy:indices.table.by:indices.table.reverse | startFrom:indices.table.page*indices.table.max | limitTo:indices.table.max">
|
||||
<td bo-text="field.field"></td>
|
||||
<td bo-text="field.mapping.type"></td>
|
||||
<tr ng-repeat="field in indexPattern.fields | orderBy:table.by:table.reverse | startFrom:table.page*table.max | limitTo:table.max">
|
||||
<td bo-text="field.name"></td>
|
||||
<td bo-text="field.type"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
5
src/kibana/apps/settings/sections.js
Normal file
5
src/kibana/apps/settings/sections.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
define(function (require) {
|
||||
// sections are defined by the controllers required by the index, and are defined by
|
||||
// the controller, but written here so that they can easily be shared
|
||||
return [];
|
||||
});
|
|
@ -1,91 +0,0 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('app/settings');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.service('indexPatterns', function (config, es, courier) {
|
||||
this.getFields = function (id) {
|
||||
if (typeof id === 'object' && typeof id.get === 'function') {
|
||||
id = id.get('index');
|
||||
}
|
||||
|
||||
return es.get({
|
||||
index: config.file.kibanaIndex,
|
||||
type: 'mapping',
|
||||
id: id
|
||||
})
|
||||
.then(function (resp) {
|
||||
return _.map(resp._source, function (v, k) {
|
||||
return {field: k, mapping: v};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.getIds = function () {
|
||||
return es.search({
|
||||
index: config.file.kibanaIndex,
|
||||
type: 'mapping',
|
||||
fields: [],
|
||||
body: {
|
||||
query: { match_all: {} }
|
||||
}
|
||||
})
|
||||
.then(function (resp) {
|
||||
return _.pluck(resp.hits.hits, '_id');
|
||||
});
|
||||
};
|
||||
|
||||
this.find = function (searchString) {
|
||||
return es.search({
|
||||
index: config.file.kibanaIndex,
|
||||
type: 'mapping',
|
||||
fields: [],
|
||||
body: {
|
||||
query: {
|
||||
multi_match: {
|
||||
query: searchString || '',
|
||||
type: 'phrase_prefix',
|
||||
fields: ['_id'],
|
||||
zero_terms_query: 'all'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function (resp) {
|
||||
return resp.hits.hits.map(function (hit) {
|
||||
return {
|
||||
id: hit._id,
|
||||
title: hit._id,
|
||||
url: '/settings/indices/' + hit._id
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.delete = function (pattern) {
|
||||
var source = courier.createSource('search').index(pattern);
|
||||
return source.clearFieldCache()
|
||||
.then(function () {
|
||||
return es.indices.refresh({
|
||||
index: config.file.kibanaIndex
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
source.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
this.create = function (pattern) {
|
||||
var source = courier.createSource('search').index(pattern);
|
||||
return source.getFields()
|
||||
.then(function (fields) {
|
||||
return es.indices.refresh({
|
||||
index: config.file.kibanaIndex
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
source.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
});
|
|
@ -14,12 +14,12 @@ define(function (require) {
|
|||
var aggs = require('../saved_visualizations/_aggs');
|
||||
var visConfigCategories = require('../saved_visualizations/_config_categories');
|
||||
|
||||
var getVisAndFieldsHash = function (savedVisualizations, courier, Notifier, $location, $route) {
|
||||
var getVisAndFields = function (savedVisualizations, courier, Notifier, $location, $route) {
|
||||
return function (using) {
|
||||
return savedVisualizations.get(using)
|
||||
.then(function (vis) {
|
||||
// get the fields before we render, but return the vis
|
||||
return vis.searchSource.getFields()
|
||||
return courier.getFieldsFor(vis.searchSource)
|
||||
.then(function (fields) {
|
||||
return [vis, fields];
|
||||
});
|
||||
|
@ -40,16 +40,16 @@ define(function (require) {
|
|||
.when('/visualize/create', {
|
||||
template: require('text!../editor.html'),
|
||||
resolve: {
|
||||
visAndFieldsHash: function ($injector, $route) {
|
||||
return $injector.invoke(getVisAndFieldsHash)($route.current.params);
|
||||
visAndFields: function ($injector, $route) {
|
||||
return $injector.invoke(getVisAndFields)($route.current.params);
|
||||
}
|
||||
}
|
||||
})
|
||||
.when('/visualize/edit/:id', {
|
||||
template: require('text!../editor.html'),
|
||||
resolve: {
|
||||
visAndFieldsHash: function ($injector, $route) {
|
||||
return $injector.invoke(getVisAndFieldsHash)($route.current.params.id);
|
||||
visAndFields: function ($injector, $route) {
|
||||
return $injector.invoke(getVisAndFields)($route.current.params.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -60,26 +60,14 @@ define(function (require) {
|
|||
});
|
||||
|
||||
// get the vis loaded in from the routes
|
||||
var vis = $route.current.locals.visAndFieldsHash[0];
|
||||
var vis = $route.current.locals.visAndFields[0];
|
||||
// vis.destroy called by visualize directive
|
||||
|
||||
var fieldsHash = $route.current.locals.visAndFieldsHash[1];
|
||||
$scope.fields = _.sortBy($route.current.locals.visAndFields[1], 'name');
|
||||
$scope.fields.byName = _.indexBy($scope.fields, 'name');
|
||||
|
||||
var $state = new AppState(vis.getState());
|
||||
|
||||
// get the current field list
|
||||
// create a sorted list of the fields for display purposes
|
||||
$scope.fields = _(fieldsHash)
|
||||
.keys()
|
||||
.sort()
|
||||
.transform(function (fields, name) {
|
||||
var field = fieldsHash[name];
|
||||
field.name = name;
|
||||
fields.push(field);
|
||||
})
|
||||
.value();
|
||||
$scope.fields.byName = fieldsHash;
|
||||
|
||||
$scope.vis = vis;
|
||||
$scope.aggs = aggs;
|
||||
$scope.visConfigCategories = visConfigCategories;
|
||||
|
|
|
@ -3,7 +3,7 @@ define(function (require) {
|
|||
var typeDefs = require('../saved_visualizations/_type_defs');
|
||||
|
||||
require('../saved_visualizations/saved_visualizations');
|
||||
require('saved_object/finder.directive');
|
||||
require('directives/saved_object_finder');
|
||||
require('apps/discover/saved_searches/saved_searches');
|
||||
|
||||
var app = require('modules').get('app/visualize', [
|
||||
|
@ -22,13 +22,13 @@ define(function (require) {
|
|||
routes.when('/visualize/step/1', {
|
||||
template: templateStep(1, require('text!../partials/wizard/step_1.html')),
|
||||
resolve: {
|
||||
indexPatternIds: function (indexPatterns) {
|
||||
return indexPatterns.getIds();
|
||||
indexPatternIds: function (courier) {
|
||||
return courier.indexPatterns.getIds();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.controller('VisualizeWizardStep1', function ($route, $scope, courier, config, $location, indexPatterns, timefilter) {
|
||||
app.controller('VisualizeWizardStep1', function ($route, $scope, $location, timefilter) {
|
||||
$scope.step2WithSearchUrl = function (hit) {
|
||||
return '#/visualize/step/2?savedSearchId=' + encodeURIComponent(hit.id);
|
||||
};
|
||||
|
|
|
@ -7,14 +7,14 @@ define(function (require) {
|
|||
var module = require('modules').get('kibana/directive');
|
||||
var chart; // set in "vis" watcher
|
||||
|
||||
module.directive('visualize', function (createNotifier) {
|
||||
module.directive('visualize', function (createNotifier, SavedVis) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function ($scope, $el) {
|
||||
$scope.$watch('vis', function (vis, prevVis) {
|
||||
if (prevVis) prevVis.destroy();
|
||||
if (prevVis && prevVis.destroy) prevVis.destroy();
|
||||
if (chart) chart.destroy();
|
||||
if (!vis) return;
|
||||
if (!(vis instanceof SavedVis)) return;
|
||||
|
||||
var notify = createNotifier({
|
||||
location: vis.typeName + ' visualization'
|
||||
|
|
|
@ -6,11 +6,9 @@ define(function (require) {
|
|||
var aggs = require('./_aggs');
|
||||
var typeDefs = require('./_type_defs');
|
||||
|
||||
require('services/root_search');
|
||||
|
||||
var module = require('modules').get('kibana/services');
|
||||
|
||||
module.factory('SavedVis', function (config, $injector, SavedObject, rootSearch, Promise, savedSearches) {
|
||||
module.factory('SavedVis', function (config, $injector, courier, Promise, savedSearches) {
|
||||
function SavedVis(opts) {
|
||||
var vis = this;
|
||||
opts = opts || {};
|
||||
|
@ -21,10 +19,10 @@ define(function (require) {
|
|||
};
|
||||
}
|
||||
|
||||
var typeDef = typeDefs.byName[opts.type];
|
||||
var parentSearch = opts.parentSearchSource || rootSearch;
|
||||
var typeDef;
|
||||
var parentSearch = opts.parentSearchSource || courier.SavedObject.rootSearch();
|
||||
|
||||
SavedObject.call(vis, {
|
||||
courier.SavedObject.call(vis, {
|
||||
type: 'visualization',
|
||||
|
||||
id: opts.id,
|
||||
|
@ -47,12 +45,6 @@ define(function (require) {
|
|||
|
||||
searchSource: true,
|
||||
|
||||
init: function () {
|
||||
configCats.forEach(function (category) {
|
||||
vis._initConfigCategory(category);
|
||||
});
|
||||
},
|
||||
|
||||
afterESResp: function setVisState() {
|
||||
if (!typeDef || vis.typeName !== typeDef.name) {
|
||||
// refresh the typeDef
|
||||
|
@ -96,7 +88,7 @@ define(function (require) {
|
|||
|
||||
vis._fillConfigsToMinimum();
|
||||
// get and cache the field list
|
||||
return vis.searchSource.getFields();
|
||||
return courier.getFieldsFor(vis.searchSource.get('index'));
|
||||
})
|
||||
.then(function () {
|
||||
return vis;
|
||||
|
@ -212,7 +204,7 @@ define(function (require) {
|
|||
*/
|
||||
vis.buildChartDataFromResponse = $injector.invoke(require('./_build_chart_data'));
|
||||
}
|
||||
inherits(SavedVis, SavedObject);
|
||||
inherits(SavedVis, courier.SavedObject);
|
||||
|
||||
return SavedVis;
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ define(function (require) {
|
|||
|
||||
require('./_saved_vis');
|
||||
|
||||
app.service('savedVisualizations', function (es, config, courier, $q, $timeout, SavedVis) {
|
||||
app.service('savedVisualizations', function (es, config, SavedVis) {
|
||||
this.get = function (id) {
|
||||
return (new SavedVis(id)).init();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var nextTick = require('utils/next_tick');
|
||||
var configFile = require('../../../config');
|
||||
|
@ -7,8 +8,7 @@ define(function (require) {
|
|||
require('notify/notify');
|
||||
|
||||
var module = require('modules').get('kibana/config', [
|
||||
'kibana/notify',
|
||||
'kibana/courier'
|
||||
'kibana/notify'
|
||||
]);
|
||||
|
||||
// guid within this window
|
||||
|
@ -23,13 +23,15 @@ define(function (require) {
|
|||
module.constant('configFile', configFile);
|
||||
|
||||
// service for delivering config variables to everywhere else
|
||||
module.service('config', function ($rootScope, createNotifier, courier, kbnVersion, configFile, Promise, setup) {
|
||||
module.service('config', function (Private, $rootScope, Notifier, kbnVersion, Promise, setup) {
|
||||
var config = this;
|
||||
var notify = createNotifier({
|
||||
var notify = new Notifier({
|
||||
location: 'Config'
|
||||
});
|
||||
|
||||
var doc = courier.createSource('doc')
|
||||
var DocSource = Private(require('courier/data_source/doc_source'));
|
||||
|
||||
var doc = (new DocSource())
|
||||
.index(configFile.kibanaIndex)
|
||||
.type('config')
|
||||
.id(kbnVersion);
|
||||
|
@ -51,13 +53,20 @@ define(function (require) {
|
|||
config.init = _.once(function () {
|
||||
notify.lifecycle('config init');
|
||||
return setup.bootstrap().then(function getDoc() {
|
||||
return doc.fetch()
|
||||
.then(function (resp) {
|
||||
if (!resp.found) {
|
||||
return doc.doIndex({}).then(getDoc);
|
||||
}
|
||||
return doc.fetch().then(function initDoc(resp) {
|
||||
if (!resp.found) return doc.doIndex({}).then(initDoc);
|
||||
else {
|
||||
vals = _.cloneDeep(resp._source || {});
|
||||
|
||||
vals = _.cloneDeep(resp._source);
|
||||
doc.onUpdate(function applyMassUpdate(resp) {
|
||||
var allKeys = [].concat(_.keys(vals), _.keys(resp._source));
|
||||
_.uniq(allKeys).forEach(function (key) {
|
||||
if (!angular.equals(vals[key], resp._source[key])) {
|
||||
change(key, resp._source[key]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
|
|
10
src/kibana/components/courier/_error_handlers.js
Normal file
10
src/kibana/components/courier/_error_handlers.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
define(function (require) {
|
||||
return function ErrorHandlerList() {
|
||||
/**
|
||||
* Queue of pending error handlers, they are removed as
|
||||
* they are resolved.
|
||||
* @type {Array}
|
||||
*/
|
||||
return [];
|
||||
};
|
||||
});
|
|
@ -1,14 +1,13 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var module = require('modules').get('kibana/courier');
|
||||
var inherits = require('utils/inherits');
|
||||
|
||||
var canStack = (function () {
|
||||
var err = new Error();
|
||||
return !!err.stack;
|
||||
}());
|
||||
var err = new Error();
|
||||
return !!err.stack;
|
||||
}());
|
||||
|
||||
module.service('couriersErrors', function () {
|
||||
return function () {
|
||||
var errors = this;
|
||||
|
||||
// abstract error class
|
||||
|
@ -139,5 +138,15 @@ define(function (require) {
|
|||
};
|
||||
inherits(errors.SavedObjectNotFound, CourierError);
|
||||
|
||||
});
|
||||
/**
|
||||
* Tried to call a method that relies on SearchSource having an indexPattern assigned
|
||||
*/
|
||||
errors.MissingIndexPattern = function MissingIndexPattern(type) {
|
||||
CourierError.call(this,
|
||||
'SearchSource expects index to be an indexPattern',
|
||||
errors.MissingIndexPattern);
|
||||
};
|
||||
inherits(errors.MissingIndexPattern, CourierError);
|
||||
|
||||
};
|
||||
});
|
10
src/kibana/components/courier/_pending_requests.js
Normal file
10
src/kibana/components/courier/_pending_requests.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
define(function (require) {
|
||||
return function PendingRequestList() {
|
||||
/**
|
||||
* Queue of pending requests, requests are removed as
|
||||
* they are processed by fetch.[sourceType]().
|
||||
* @type {Array}
|
||||
*/
|
||||
return [];
|
||||
};
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
define(function (require) {
|
||||
return function (start, end, interval, pattern) {
|
||||
|
||||
if (end.isBefore(start)) {
|
||||
throw new Error('Start must begin before end.');
|
||||
}
|
||||
|
||||
if (!~['hour', 'day', 'week', 'year'].indexOf(interval)) {
|
||||
throw new Error('Interval must be hour, day, week, or year.');
|
||||
}
|
||||
|
||||
if (!pattern) {
|
||||
throw new Error('Pattern can not be empty.');
|
||||
}
|
||||
|
||||
var data = [];
|
||||
while (start.isBefore(end)) {
|
||||
start.add(interval, '1');
|
||||
data.push(start.format(pattern));
|
||||
}
|
||||
return data;
|
||||
};
|
||||
});
|
|
@ -1,171 +1,122 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
require('./data_source/doc');
|
||||
require('./data_source/search');
|
||||
require('./fetch/fetch');
|
||||
require('./errors');
|
||||
require('./looper');
|
||||
|
||||
require('services/es');
|
||||
require('services/promises');
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
|
||||
module.service('courier', [
|
||||
'es',
|
||||
'$rootScope',
|
||||
'couriersFetch',
|
||||
'Promise',
|
||||
'Looper',
|
||||
'couriersErrors',
|
||||
'CouriersMapper',
|
||||
'CouriersDocSource',
|
||||
'CouriersSearchSource',
|
||||
function (client, $rootScope, fetch, Promise, Looper, errors, Mapper, DocSource, SearchSource) {
|
||||
var HastyRefresh = errors.HastyRefresh;
|
||||
module.service('courier', function ($rootScope, Private, Promise) {
|
||||
var DocSource = Private(require('./data_source/doc_source'));
|
||||
var SearchSource = Private(require('./data_source/search_source'));
|
||||
|
||||
function Courier() {
|
||||
var courier = this;
|
||||
courier.errors = errors;
|
||||
var SavedObject = Private(require('./saved_object/saved_object'));
|
||||
|
||||
/**
|
||||
* Queue of pending requests, requests are removed as
|
||||
* they are processed by fetch.[sourceType]().
|
||||
* @type {Array}
|
||||
*/
|
||||
courier._pendingRequests = [];
|
||||
var errors = Private(require('./_errors'));
|
||||
var pendingRequests = Private(require('./_pending_requests'));
|
||||
var searchLooper = Private(require('./looper/search'));
|
||||
var docLooper = Private(require('./looper/doc'));
|
||||
var indexPatterns = Private(require('./index_patterns/index_patterns'));
|
||||
|
||||
/**
|
||||
* Queue of pending error handlers, they are removed as
|
||||
* they are resolved.
|
||||
* @type {Array}
|
||||
*/
|
||||
courier._errorHandlers = [];
|
||||
var HastyRefresh = errors.HastyRefresh;
|
||||
|
||||
/**
|
||||
* Fetch the docs
|
||||
* @type {function}
|
||||
*/
|
||||
var processDocRequests = _.partial(fetch.docs, courier);
|
||||
function Courier() {
|
||||
var courier = this;
|
||||
|
||||
/**
|
||||
* Fetch the search requests
|
||||
* @type {function}
|
||||
*/
|
||||
var processSearchRequests = (function () {
|
||||
// track the currently executing search resquest
|
||||
var _activeAutoSearch = null;
|
||||
// expose some internal modules
|
||||
courier.errors = errors;
|
||||
courier.SavedObject = SavedObject;
|
||||
courier.indexPatterns = indexPatterns;
|
||||
|
||||
return function () {
|
||||
// fatal if refreshes take longer then the refresh interval
|
||||
if (_activeAutoSearch) Promise.rejected(new HastyRefresh());
|
||||
return _activeAutoSearch = fetch.searches(courier).finally(function (res) {
|
||||
_activeAutoSearch = null;
|
||||
});
|
||||
};
|
||||
}());
|
||||
/**
|
||||
* update the time between automatic search requests
|
||||
*
|
||||
* @chainable
|
||||
*/
|
||||
courier.fetchInterval = function (ms) {
|
||||
searchLooper.ms(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Looper which will manage the doc fetch interval
|
||||
* @type {Looper}
|
||||
*/
|
||||
var docInterval = new Looper().fn(processDocRequests).start();
|
||||
/**
|
||||
* Start fetching search requests on an interval
|
||||
* @chainable
|
||||
*/
|
||||
courier.start = function () {
|
||||
searchLooper.start();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Looper which will manage the doc fetch interval
|
||||
* @type {Looper}
|
||||
*/
|
||||
var searchInterval = new Looper().fn(processSearchRequests);
|
||||
|
||||
/**
|
||||
* Instance of Mapper, helps dataSources figure out their fields
|
||||
* @type {Mapper}
|
||||
*/
|
||||
courier._mapper = new Mapper(courier);
|
||||
|
||||
/**
|
||||
* update the time between automatic search requests
|
||||
*
|
||||
* @chainable
|
||||
*/
|
||||
courier.fetchInterval = function (ms) {
|
||||
searchInterval.ms(ms);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start fetching search requests on an interval
|
||||
* @chainable
|
||||
*/
|
||||
courier.start = function () {
|
||||
searchInterval.start();
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Process the pending request queue right now, returns
|
||||
* a promise that resembles the success of the fetch completing,
|
||||
* individual errors are routed to their respectiv requests.
|
||||
*/
|
||||
courier.fetch = function () {
|
||||
return processSearchRequests();
|
||||
};
|
||||
/**
|
||||
* Process the pending request queue right now, returns
|
||||
* a promise that resembles the success of the fetch completing,
|
||||
* individual errors are routed to their respectiv requests.
|
||||
*/
|
||||
courier.fetch = function () {
|
||||
return searchLooper.run();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* is the currior currently fetching search
|
||||
* results automatically?
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
courier.started = function () {
|
||||
return searchInterval.started();
|
||||
};
|
||||
/**
|
||||
* is the currior currently fetching search
|
||||
* results automatically?
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
courier.started = function () {
|
||||
return searchLooper.started();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* stop the courier from fetching more search
|
||||
* results, does not stop vaidating docs.
|
||||
*
|
||||
* @chainable
|
||||
*/
|
||||
courier.stop = function () {
|
||||
searchInterval.stop();
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* stop the courier from fetching more search
|
||||
* results, does not stop vaidating docs.
|
||||
*
|
||||
* @chainable
|
||||
*/
|
||||
courier.stop = function () {
|
||||
searchLooper.stop();
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* create a source object that is a child of this courier
|
||||
*
|
||||
* @param {string} type - the type of Source to create
|
||||
*/
|
||||
courier.createSource = function (type) {
|
||||
switch (type) {
|
||||
case 'doc':
|
||||
return new DocSource(courier);
|
||||
case 'search':
|
||||
return new SearchSource(courier);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* create a source object that is a child of this courier
|
||||
*
|
||||
* @param {string} type - the type of Source to create
|
||||
*/
|
||||
courier.createSource = function (type) {
|
||||
switch (type) {
|
||||
case 'doc':
|
||||
return new DocSource();
|
||||
case 'search':
|
||||
return new SearchSource();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Abort all pending requests
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
courier.close = function () {
|
||||
[].concat(this._pendingRequests.splice(0), this._errorHandlers.splice(0))
|
||||
.forEach(function (req) {
|
||||
req.defer.reject(new errors.Abort());
|
||||
});
|
||||
courier.getFieldsFor = function (indexish) {
|
||||
return indexPatterns.getFieldsFor(indexish);
|
||||
};
|
||||
|
||||
if (this._pendingRequests.length) {
|
||||
throw new Error('Aborting all pending requests failed.');
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Abort all pending requests
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
courier.close = function () {
|
||||
searchLooper.stop();
|
||||
docLooper.stop();
|
||||
|
||||
return new Courier();
|
||||
[].concat(pendingRequests.splice(0), this._errorHandlers.splice(0))
|
||||
.forEach(function (req) {
|
||||
req.defer.reject(new errors.Abort());
|
||||
});
|
||||
|
||||
if (pendingRequests.length) {
|
||||
throw new Error('Aborting all pending requests failed.');
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
return new Courier();
|
||||
});
|
||||
});
|
|
@ -1,14 +1,14 @@
|
|||
define(function (require) {
|
||||
var inherits = require('utils/inherits');
|
||||
var _ = require('lodash');
|
||||
var Mapper = require('courier/mapper');
|
||||
var nextTick = require('utils/next_tick');
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
return function SourceAbstractFactory(Private, Promise, timefilter) {
|
||||
var pendingRequests = Private(require('../_pending_requests'));
|
||||
var errorHandlers = Private(require('../_error_handlers'));
|
||||
var fetch = Private(require('../fetch/fetch'));
|
||||
|
||||
module.factory('CouriersSourceAbstract', function (couriersFetch, Promise, timefilter) {
|
||||
|
||||
function SourceAbstract(courier, initialState) {
|
||||
function SourceAbstract(initialState) {
|
||||
this._state = (function () {
|
||||
// state can be serialized as JSON, and passed back in to restore
|
||||
if (initialState) {
|
||||
|
@ -23,7 +23,6 @@ define(function (require) {
|
|||
}());
|
||||
|
||||
this._dynamicState = this._dynamicState || {};
|
||||
this._courier = courier;
|
||||
|
||||
// get/set internal state values
|
||||
this._methods.forEach(function (name) {
|
||||
|
@ -69,43 +68,13 @@ define(function (require) {
|
|||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the courier for this dataSource
|
||||
* @chainable
|
||||
* @param {Courier} newCourier
|
||||
*/
|
||||
SourceAbstract.prototype.courier = function (newCourier) {
|
||||
this._courier = newCourier;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new dataSource object of the same type
|
||||
* as this, which inherits this dataSource's properties
|
||||
* @return {SourceAbstract}
|
||||
*/
|
||||
SourceAbstract.prototype.extend = function () {
|
||||
return this._courier
|
||||
.createSource(this._getType())
|
||||
.inherits(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* fetch the field names for this SourceAbstract
|
||||
* @param {Function} cb
|
||||
* @callback {Error, Array} - calls cb with a possible error or an array of field names
|
||||
*/
|
||||
SourceAbstract.prototype.getFields = function () {
|
||||
return this._courier._mapper.getFields(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* clear the field list cache
|
||||
* @param {Function} cb
|
||||
* @callback {Error, Array} - calls cb with a possible error
|
||||
*/
|
||||
SourceAbstract.prototype.clearFieldCache = function () {
|
||||
return this._courier._mapper.clearCache(this);
|
||||
return (new this.Class()).inherits(this);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -132,7 +101,7 @@ define(function (require) {
|
|||
SourceAbstract.prototype.onResults = function (handler) {
|
||||
var source = this;
|
||||
return new Promise.emitter(function (resolve, reject, defer) {
|
||||
source._courier._pendingRequests.push({
|
||||
pendingRequests.push({
|
||||
source: source,
|
||||
defer: defer
|
||||
});
|
||||
|
@ -147,7 +116,7 @@ define(function (require) {
|
|||
*/
|
||||
SourceAbstract.prototype.onError = function () {
|
||||
var defer = Promise.defer();
|
||||
this._courier._errorHandlers.push({
|
||||
errorHandlers.push({
|
||||
source: this,
|
||||
defer: defer
|
||||
});
|
||||
|
@ -159,15 +128,14 @@ define(function (require) {
|
|||
* @param {Function} cb - callback
|
||||
*/
|
||||
SourceAbstract.prototype.fetch = function () {
|
||||
var courier = this._courier;
|
||||
var source = this;
|
||||
return couriersFetch[this._getType()](this)
|
||||
return fetch[this._getType()](this)
|
||||
.then(function (res) {
|
||||
courier._pendingRequests.splice(0).forEach(function (req) {
|
||||
pendingRequests.splice(0).forEach(function (req) {
|
||||
if (req.source === source) {
|
||||
req.defer.resolve(_.cloneDeep(res));
|
||||
} else {
|
||||
courier._pendingRequests.push(req);
|
||||
pendingRequests.push(req);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
|
@ -179,8 +147,8 @@ define(function (require) {
|
|||
* @return {undefined}
|
||||
*/
|
||||
SourceAbstract.prototype.cancelPending = function () {
|
||||
var pending = _.where(this._courier._pendingRequests, { source: this});
|
||||
_.pull.apply(_, [this._courier._pendingRequests].concat(pending));
|
||||
var pending = _.where(pendingRequests, { source: this});
|
||||
_.pull.apply(_, [pendingRequests].concat(pending));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -216,9 +184,17 @@ define(function (require) {
|
|||
// call the ittr and return it's promise
|
||||
return (function ittr(resolve, reject) {
|
||||
// itterate the _state object (not array) and
|
||||
// pass each key:value pair to collect prop.
|
||||
return Promise.all(_.map(current._state, function (value, key) {
|
||||
return root._mergeProp(flatState, value, key);
|
||||
// pass each key:value pair to source._mergeProp. if _mergeProp
|
||||
// returns a promise, then wait for it to complete and call _mergeProp again
|
||||
return Promise.all(_.map(current._state, function ittr(value, key) {
|
||||
if (Promise.is(value)) {
|
||||
return value.then(function (value) {
|
||||
return ittr(value, key);
|
||||
});
|
||||
}
|
||||
|
||||
var prom = root._mergeProp(flatState, value, key);
|
||||
return Promise.is(prom) ? prom : null;
|
||||
}))
|
||||
.then(function () {
|
||||
// move to this sources parent
|
||||
|
@ -261,5 +237,5 @@ define(function (require) {
|
|||
|
||||
return SourceAbstract;
|
||||
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,7 +1,9 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
return function (Promise, es) {
|
||||
return function (Promise, Private, es) {
|
||||
var errors = Private(require('../_errors'));
|
||||
var pendingRequests = Private(require('../_pending_requests'));
|
||||
|
||||
/**
|
||||
* Backend for doUpdate and doIndex
|
||||
|
@ -10,7 +12,7 @@ define(function (require) {
|
|||
* of the the docs current version be sent to es?
|
||||
* @param {String} body - HTTP request body
|
||||
*/
|
||||
return function (courier, method, validateVersion, body) {
|
||||
return function (method, validateVersion, body) {
|
||||
var doc = this;
|
||||
// straight assignment will causes undefined values
|
||||
var params = _.pick(this._state, ['id', 'type', 'index']);
|
||||
|
@ -23,7 +25,7 @@ define(function (require) {
|
|||
|
||||
return es[method](params)
|
||||
.then(function (resp) {
|
||||
if (resp.status === 409) throw new courier.errors.VersionConflict(resp);
|
||||
if (resp.status === 409) throw new errors.VersionConflict(resp);
|
||||
|
||||
doc._storeVersion(resp._version);
|
||||
doc.id(resp._id);
|
||||
|
@ -40,12 +42,9 @@ define(function (require) {
|
|||
// use the key to compair sources
|
||||
var key = doc._versionKey();
|
||||
|
||||
// filter out the matching requests from the _pendingRequests queue
|
||||
var pending = courier._pendingRequests;
|
||||
|
||||
// clear the queue and filter out the removed items, pushing the
|
||||
// unmatched ones back in.
|
||||
pending.splice(0).filter(function (req) {
|
||||
pendingRequests.splice(0).filter(function (req) {
|
||||
var isDoc = req.source._getType() === 'doc';
|
||||
var keyMatches = isDoc && req.source._versionKey() === key;
|
||||
|
||||
|
@ -56,7 +55,7 @@ define(function (require) {
|
|||
}
|
||||
|
||||
// otherwise, put the request back into the queue
|
||||
pending.push(req);
|
||||
pendingRequests.push(req);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -64,7 +63,7 @@ define(function (require) {
|
|||
})
|
||||
.catch(function (err) {
|
||||
// cast the error
|
||||
throw new courier.errors.RequestFailure(err);
|
||||
throw new errors.RequestFailure(err);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,25 +3,22 @@ define(function (require) {
|
|||
|
||||
var inherits = require('utils/inherits');
|
||||
|
||||
require('./abstract');
|
||||
return function DocSourceFactory(Private, Promise, es) {
|
||||
var errors = Private(require('../_errors'));
|
||||
var sendToEs = Private(require('./_doc_send_to_es'));
|
||||
var SourceAbstract = Private(require('./_abstract'));
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
var VersionConflict = errors.VersionConflict;
|
||||
var RequestFailure = errors.RequestFailure;
|
||||
|
||||
module.factory('CouriersDocSource', function (couriersErrors, CouriersSourceAbstract, Promise, es, $injector) {
|
||||
var VersionConflict = couriersErrors.VersionConflict;
|
||||
var RequestFailure = couriersErrors.RequestFailure;
|
||||
var sendToEs = $injector.invoke(require('./_doc_send_to_es'));
|
||||
|
||||
function DocSource(courier, initialState) {
|
||||
CouriersSourceAbstract.call(this, courier, initialState);
|
||||
function DocSource(initialState) {
|
||||
SourceAbstract.call(this, initialState);
|
||||
|
||||
// move onResults over to onUpdate, because that makes more sense
|
||||
this.onUpdate = this.onResults;
|
||||
this.onResults = void 0;
|
||||
|
||||
this._sendToEs = sendToEs;
|
||||
}
|
||||
inherits(DocSource, CouriersSourceAbstract);
|
||||
inherits(DocSource, SourceAbstract);
|
||||
|
||||
/*****
|
||||
* PUBLIC API
|
||||
|
@ -46,7 +43,7 @@ define(function (require) {
|
|||
*/
|
||||
DocSource.prototype.doUpdate = function (fields) {
|
||||
if (!this._state.id) return this.doIndex(fields);
|
||||
return this._sendToEs(this._courier, 'update', false, { doc: fields });
|
||||
return sendToEs.call(this, 'update', false, { doc: fields });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -55,7 +52,7 @@ define(function (require) {
|
|||
* @return {[type]} [description]
|
||||
*/
|
||||
DocSource.prototype.doIndex = function (body) {
|
||||
return this._sendToEs(this._courier, 'index', false, body);
|
||||
return sendToEs.call(this, 'index', false, body);
|
||||
};
|
||||
|
||||
/*****
|
||||
|
@ -154,5 +151,5 @@ define(function (require) {
|
|||
};
|
||||
|
||||
return DocSource;
|
||||
});
|
||||
};
|
||||
});
|
|
@ -2,19 +2,18 @@ define(function (require) {
|
|||
var inherits = require('utils/inherits');
|
||||
var _ = require('lodash');
|
||||
|
||||
require('./abstract');
|
||||
return function SearchSourceFactory(Promise, Private) {
|
||||
var errors = Private(require('../_errors'));
|
||||
var SourceAbstract = Private(require('./_abstract'));
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
var FetchFailure = errors.FetchFailure;
|
||||
var RequestFailure = errors.RequestFailure;
|
||||
var MissingIndexPattern = errors.MissingIndexPattern;
|
||||
|
||||
module.factory('CouriersSearchSource', function (couriersErrors, CouriersSourceAbstract, Promise) {
|
||||
var FetchFailure = couriersErrors.FetchFailure;
|
||||
var RequestFailure = couriersErrors.RequestFailure;
|
||||
|
||||
|
||||
function SearchSource(courier, initialState) {
|
||||
CouriersSourceAbstract.call(this, courier, initialState);
|
||||
function SearchSource(initialState) {
|
||||
SourceAbstract.call(this, initialState);
|
||||
}
|
||||
inherits(SearchSource, CouriersSourceAbstract);
|
||||
inherits(SearchSource, SourceAbstract);
|
||||
|
||||
/*****
|
||||
* PUBLIC API
|
||||
|
@ -40,6 +39,10 @@ define(function (require) {
|
|||
'source'
|
||||
];
|
||||
|
||||
SearchSource.prototype.extend = function () {
|
||||
return (new SearchSource()).inherits(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a searchSource that this source should inherit from
|
||||
* @param {SearchSource} searchSource - the parent searchSource
|
||||
|
@ -58,15 +61,6 @@ define(function (require) {
|
|||
return this._parent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the mapping for the index of this SearchSource
|
||||
* @return {Promise}
|
||||
*/
|
||||
SearchSource.prototype.getFields = function () {
|
||||
return this._courier._mapper.getFields(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporarily prevent this Search from being fetched... not a fan but it's easy
|
||||
*/
|
||||
|
@ -105,8 +99,9 @@ define(function (require) {
|
|||
SearchSource.prototype._mergeProp = function (state, val, key) {
|
||||
if (typeof val === 'function') {
|
||||
var source = this;
|
||||
return Promise.cast(val(source)).then(function (res) {
|
||||
return source._mergeProp(state, res, key);
|
||||
return Promise.cast(val(this))
|
||||
.then(function (newVal) {
|
||||
return source._mergeProp(state, newVal, key);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,5 +131,5 @@ define(function (require) {
|
|||
};
|
||||
|
||||
return SearchSource;
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
define(function (require) {
|
||||
return function RequestErrorHandlerFactory(Private, Notifier) {
|
||||
var pendingRequests = Private(require('../_pending_requests'));
|
||||
var errorHandlers = Private(require('../_error_handlers'));
|
||||
|
||||
var notify = new Notifier({
|
||||
location: 'Courier Fetch Error'
|
||||
});
|
||||
|
||||
function RequestErrorHandler() {}
|
||||
|
||||
RequestErrorHandler.prototype.handle = function (req, error) {
|
||||
pendingRequests.push(req);
|
||||
|
||||
var handlerCount = 0;
|
||||
errorHandlers.splice(0).forEach(function (handler) {
|
||||
if (handler.source !== req.source) return pendingRequests.push(handler);
|
||||
handler.defer.resolve(error);
|
||||
handlerCount++;
|
||||
});
|
||||
|
||||
if (!handlerCount) {
|
||||
notify.fatal(new Error('unhandled error ' + (error.stack || error.message)));
|
||||
}
|
||||
};
|
||||
|
||||
return RequestErrorHandler;
|
||||
};
|
||||
});
|
|
@ -1,32 +1,18 @@
|
|||
define(function (require) {
|
||||
return function fetchService(Private, es, Promise, Notifier, $q) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
var _ = require('lodash');
|
||||
var docStrategy = require('./strategy/doc');
|
||||
var searchStrategy = require('./strategy/search');
|
||||
|
||||
var docStrategy = require('./strategy/doc');
|
||||
var searchStrategy = require('./strategy/search');
|
||||
var errors = Private(require('../_errors'));
|
||||
var RequestErrorHandler = Private(require('./_request_error_handler'));
|
||||
var pendingRequests = Private(require('../_pending_requests'));
|
||||
|
||||
module.service('couriersFetch', function (es, Promise, couriersErrors, createNotifier, $q) {
|
||||
var notify = createNotifier({
|
||||
var notify = new Notifier({
|
||||
location: 'Courier Fetch'
|
||||
});
|
||||
|
||||
function RequestErrorHandler(courier) { this._courier = courier; }
|
||||
RequestErrorHandler.prototype.handle = function (req, error) {
|
||||
if (!this._courier) return req.defer.reject(error);
|
||||
this._courier._pendingRequests.push(req);
|
||||
var handlerCount = 0;
|
||||
this._courier._errorHandlers.splice(0).forEach(function (handler) {
|
||||
if (handler.source !== req.source) return this._courier._pendingRequests.push(handler);
|
||||
handler.defer.resolve(error);
|
||||
handlerCount++;
|
||||
});
|
||||
if (!handlerCount) {
|
||||
notify.fatal(new Error('unhandled error ' + (error.stack || error.message)));
|
||||
this._courier.stop();
|
||||
}
|
||||
};
|
||||
|
||||
var fetchThese = function (strategy, requests, reqErrHandler) {
|
||||
var all, body;
|
||||
|
||||
|
@ -47,7 +33,7 @@ define(function (require) {
|
|||
.then(function (resp) {
|
||||
strategy.getResponses(resp).forEach(function (resp) {
|
||||
var req = all.shift();
|
||||
if (resp.error) return reqErrHandler.handle(req, new couriersErrors.FetchFailure(resp));
|
||||
if (resp.error) return reqErrHandler.handle(req, new errors.FetchFailure(resp));
|
||||
else strategy.resolveRequest(req, resp);
|
||||
});
|
||||
|
||||
|
@ -63,10 +49,10 @@ define(function (require) {
|
|||
}, notify.fatal);
|
||||
};
|
||||
|
||||
var fetchPending = function (strategy, courier) {
|
||||
var requests = strategy.getPendingRequests(courier._pendingRequests);
|
||||
var fetchPending = function (strategy) {
|
||||
var requests = strategy.getPendingRequests(pendingRequests);
|
||||
if (!requests.length) return Promise.resolved();
|
||||
else return fetchThese(strategy, requests, new RequestErrorHandler(courier));
|
||||
else return fetchThese(strategy, requests, new RequestErrorHandler());
|
||||
};
|
||||
|
||||
var fetchASource = function (strategy, source) {
|
||||
|
@ -109,5 +95,5 @@ define(function (require) {
|
|||
* @async
|
||||
*/
|
||||
this.search = _.partial(fetchASource, searchStrategy);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
define(function (require) {
|
||||
return function CastMappingTypeFn() {
|
||||
/**
|
||||
* Accepts a mapping type, and converts it into it's js equivilent
|
||||
* @param {String} type - the type from the mapping's 'type' field
|
||||
* @return {String} - the most specific type that we care for
|
||||
*/
|
||||
return function castMappingType(type) {
|
||||
switch (type) {
|
||||
case 'float':
|
||||
case 'double':
|
||||
case 'integer':
|
||||
case 'long':
|
||||
case 'short':
|
||||
case 'byte':
|
||||
case 'token_count':
|
||||
return 'number';
|
||||
case 'date':
|
||||
case 'boolean':
|
||||
case 'ip':
|
||||
case 'attachment':
|
||||
case 'geo_point':
|
||||
case 'geo_shape':
|
||||
return type;
|
||||
default: // including 'string'
|
||||
return 'string';
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
define(function (require) {
|
||||
var inherits = require('utils/inherits');
|
||||
|
||||
return function IndexPatternFactory(Private) {
|
||||
var SavedObject = Private(require('../saved_object/saved_object'));
|
||||
var mapper = Private(require('./_mapper'));
|
||||
|
||||
function IndexPattern(id) {
|
||||
var pattern = this;
|
||||
SavedObject.call(pattern, {
|
||||
type: 'index-pattern',
|
||||
|
||||
id: id,
|
||||
|
||||
mapping: {
|
||||
title: 'string',
|
||||
timeFieldName: 'string',
|
||||
fields: 'json'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
title: id
|
||||
},
|
||||
|
||||
afterESResp: function () {
|
||||
if (pattern.id && !pattern.fields) {
|
||||
return pattern.refreshFields();
|
||||
}
|
||||
},
|
||||
|
||||
searchSource: false
|
||||
});
|
||||
|
||||
pattern.refreshFields = function () {
|
||||
return mapper.clearCache(pattern.id)
|
||||
.then(function () {
|
||||
return mapper.getFieldsForIndexPattern(pattern.id);
|
||||
})
|
||||
.then(function (fields) {
|
||||
pattern.fields = fields;
|
||||
return pattern.save();
|
||||
});
|
||||
};
|
||||
|
||||
pattern.toJSON = function () {
|
||||
return pattern.id;
|
||||
};
|
||||
|
||||
pattern.toString = function () {
|
||||
return '' + this.toJSON();
|
||||
};
|
||||
}
|
||||
return IndexPattern;
|
||||
};
|
||||
});
|
|
@ -1,11 +1,10 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('kibana/courier');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.factory('LocalCache', function () {
|
||||
return function LocalCacheFactory() {
|
||||
function LocalCache(opts) {
|
||||
|
||||
var _id = opts.id || _.identity;
|
||||
opts = opts || {};
|
||||
var _id = opts.id || function (o) { return '' + o; };
|
||||
var _cache = {};
|
||||
|
||||
this.get = function (obj) {
|
||||
|
@ -30,6 +29,8 @@ define(function (require) {
|
|||
delete _cache[id];
|
||||
};
|
||||
}
|
||||
|
||||
return LocalCache;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
61
src/kibana/components/courier/index_patterns/_mapper.js
Normal file
61
src/kibana/components/courier/index_patterns/_mapper.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
return function MapperService(Private, Promise, es, configFile) {
|
||||
var errors = Private(require('../_errors'));
|
||||
|
||||
var CacheWriteFailure = errors.CacheWriteFailure;
|
||||
var MappingConflict = errors.MappingConflict;
|
||||
var RestrictedMapping = errors.RestrictedMapping;
|
||||
var FieldNotFoundInCache = errors.FieldNotFoundInCache;
|
||||
|
||||
// private function
|
||||
var transformMappingIntoFields = Private(require('./_transform_mapping_into_fields'));
|
||||
// private factory
|
||||
var LocalCache = Private(require('./_local_cache'));
|
||||
|
||||
function Mapper() {
|
||||
|
||||
// Save a reference to mapper
|
||||
var mapper = this;
|
||||
|
||||
// proper-ish cache, keeps a clean copy of the object, only returns copies of it's copy
|
||||
var fieldCache = new LocalCache();
|
||||
|
||||
/**
|
||||
* Gets an object containing all fields with their mappings
|
||||
* @param {dataSource} dataSource
|
||||
* @returns {Promise}
|
||||
* @async
|
||||
*/
|
||||
mapper.getFieldsForIndexPattern = function (indexPattern) {
|
||||
var cache;
|
||||
if (cache = fieldCache.get(indexPattern)) return Promise.resolved(cache);
|
||||
|
||||
return es.indices.getFieldMapping({
|
||||
// TODO: Change index to be the resolved in some way, last three months, last hour, last year, whatever
|
||||
index: indexPattern,
|
||||
field: '*',
|
||||
})
|
||||
.then(transformMappingIntoFields)
|
||||
.then(function (fields) {
|
||||
fieldCache.set(indexPattern, fields);
|
||||
return fieldCache.get(indexPattern);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears mapping caches from elasticsearch and from local object
|
||||
* @param {dataSource} dataSource
|
||||
* @returns {Promise}
|
||||
* @async
|
||||
*/
|
||||
mapper.clearCache = function (indexPattern) {
|
||||
fieldCache.clear(indexPattern);
|
||||
return Promise.resolved();
|
||||
};
|
||||
}
|
||||
|
||||
return new Mapper();
|
||||
};
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
define(function (require) {
|
||||
return function transformMappingIntoFields(Private, configFile) {
|
||||
var _ = require('lodash');
|
||||
var MappingConflict = Private(require('../_errors')).MappingConflict;
|
||||
var castMappingType = Private(require('./_cast_mapping_type'));
|
||||
|
||||
var reservedFields = {
|
||||
_id: { type: 'string'},
|
||||
_type: { type: 'string' },
|
||||
_index: { type: 'string' }
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the ES response into the simple map for fields to
|
||||
* mappings which we will cache
|
||||
*
|
||||
* @param {object} response - complex, excessively nested
|
||||
* object returned from ES
|
||||
* @return {object} - simple object that works for all of kibana
|
||||
* use-cases
|
||||
*/
|
||||
return function (response) {
|
||||
var fields = _.cloneDeep(reservedFields);
|
||||
_.each(response, function (index, indexName) {
|
||||
if (indexName === configFile.kibanaIndex) return;
|
||||
_.each(index.mappings, function (mappings, typeName) {
|
||||
_.each(mappings, function (field, name) {
|
||||
var keys = Object.keys(field.mapping);
|
||||
if (keys.length === 0 || name[0] === '_') return;
|
||||
|
||||
var mapping = _.cloneDeep(field.mapping[keys.shift()]);
|
||||
mapping.type = castMappingType(mapping.type);
|
||||
|
||||
if (fields[name]) {
|
||||
if (fields[name].type !== mapping.type) {
|
||||
throw new MappingConflict(name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fields[name] = mapping;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return _.map(fields, function (mapping, name) {
|
||||
mapping.name = name;
|
||||
return mapping;
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
define(function (require) {
|
||||
return function IndexPatternsService(configFile, es, Notifier, Private, Promise) {
|
||||
var indexPatterns = this;
|
||||
var _ = require('lodash');
|
||||
|
||||
var IndexPattern = Private(require('./_index_pattern'));
|
||||
var SourceAbstract = Private(require('../data_source/_abstract'));
|
||||
|
||||
var mapper = Private(require('./_mapper'));
|
||||
|
||||
var notify = new Notifier({ location: 'IndexPatterns Service'});
|
||||
|
||||
indexPatterns.get = _.memoize(function (id) {
|
||||
return (new IndexPattern(id)).init();
|
||||
});
|
||||
|
||||
indexPatterns.getIds = function () {
|
||||
return es.search({
|
||||
index: configFile.kibanaIndex,
|
||||
type: 'index-pattern',
|
||||
fields: [],
|
||||
body: {
|
||||
query: { match_all: {} }
|
||||
}
|
||||
})
|
||||
.then(function (resp) {
|
||||
return _.pluck(resp.hits.hits, '_id');
|
||||
});
|
||||
};
|
||||
|
||||
indexPatterns.delete = function (pattern) {
|
||||
return es.delete({
|
||||
index: configFile.kibanaIndex,
|
||||
type: 'index-pattern',
|
||||
id: pattern.id
|
||||
});
|
||||
};
|
||||
|
||||
indexPatterns.getFieldsFor = function (indexish) {
|
||||
// pull the index string out of Source objects
|
||||
if (indexish instanceof SourceAbstract) {
|
||||
indexish = indexish.get('index');
|
||||
}
|
||||
|
||||
return Promise.cast(
|
||||
(typeof indexish === 'object')
|
||||
? indexish
|
||||
: indexPatterns.get(indexish)
|
||||
)
|
||||
.then(function (indexPattern) {
|
||||
return indexPattern.fields;
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,9 +1,7 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
|
||||
module.factory('Looper', function ($timeout) {
|
||||
return function LooperFactory($timeout, Notifier) {
|
||||
var _ = require('lodash');
|
||||
var notify = new Notifier();
|
||||
|
||||
function Looper(ms, fn) {
|
||||
var _ms = ms === void 0 ? 1500 : ms;
|
||||
|
@ -108,8 +106,15 @@ define(function (require) {
|
|||
|
||||
_timerId = _ms ? $timeout(looper._looperOver, _ms) : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* execute the _fn, and restart the timer
|
||||
*/
|
||||
looper.run = function () {
|
||||
looper.start();
|
||||
};
|
||||
}
|
||||
|
||||
return Looper;
|
||||
});
|
||||
};
|
||||
});
|
14
src/kibana/components/courier/looper/doc.js
Normal file
14
src/kibana/components/courier/looper/doc.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
define(function (require) {
|
||||
return function DocLooperService(Private) {
|
||||
var fetch = Private(require('../fetch/fetch'));
|
||||
var Looper = Private(require('./_looper'));
|
||||
|
||||
/**
|
||||
* The Looper which will manage the doc fetch interval
|
||||
* @type {Looper}
|
||||
*/
|
||||
var docLooper = new Looper(1500, fetch.docs).start();
|
||||
|
||||
return docLooper;
|
||||
};
|
||||
});
|
24
src/kibana/components/courier/looper/search.js
Normal file
24
src/kibana/components/courier/looper/search.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
define(function (require) {
|
||||
return function SearchLooperService(Private, Promise) {
|
||||
var fetch = Private(require('../fetch/fetch'));
|
||||
var Looper = Private(require('./_looper'));
|
||||
var errors = Private(require('../_errors'));
|
||||
|
||||
// track the currently executing search resquest
|
||||
var _activeAutoSearch = null;
|
||||
|
||||
/**
|
||||
* The Looper which will manage the doc fetch interval
|
||||
* @type {Looper}
|
||||
*/
|
||||
var searchLooper = new Looper(null, function () {
|
||||
// fatal if refreshes take longer then the refresh interval
|
||||
if (_activeAutoSearch) Promise.rejected(new errors.HastyRefresh());
|
||||
return _activeAutoSearch = fetch.searches().finally(function (res) {
|
||||
_activeAutoSearch = null;
|
||||
});
|
||||
}).start();
|
||||
|
||||
return searchLooper;
|
||||
};
|
||||
});
|
|
@ -1,187 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
require('./local_cache');
|
||||
|
||||
// these fields are the only _ prefixed fields that will
|
||||
// be found in the field list. All others are filtered
|
||||
var reservedFields = {
|
||||
_id: { type: 'string' },
|
||||
_type: { type: 'string' },
|
||||
_index: { type: 'string' }
|
||||
};
|
||||
|
||||
var module = require('modules').get('kibana/courier');
|
||||
|
||||
module.factory('CouriersMapper', function (Promise, es, configFile, LocalCache, couriersErrors) {
|
||||
var CacheWriteFailure = couriersErrors.CacheWriteFailure;
|
||||
var MappingConflict = couriersErrors.MappingConflict;
|
||||
var RestrictedMapping = couriersErrors.RestrictedMapping;
|
||||
var FieldNotFoundInCache = couriersErrors.FieldNotFoundInCache;
|
||||
|
||||
function CouriersMapper(courier) {
|
||||
|
||||
// Save a reference to mapper
|
||||
var mapper = this;
|
||||
|
||||
var fieldCache = new LocalCache({
|
||||
id: function (dataSource) {
|
||||
return dataSource.get('index');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets an object containing all fields with their mappings
|
||||
* @param {dataSource} dataSource
|
||||
* @async
|
||||
*/
|
||||
mapper.getFields = function (dataSource) {
|
||||
if (!dataSource.get('index')) {
|
||||
// always callback async
|
||||
return Promise.rejected(new TypeError('dataSource must have an index before it\'s fields can be fetched'));
|
||||
}
|
||||
|
||||
return mapper.getCachedFieldsFor(dataSource)
|
||||
.catch(function () {
|
||||
// If we are unable to get the fields from cache, get them from mapping instead
|
||||
return mapper.getFieldsFromEsFor(dataSource);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an object containing all fields with their mappings from kibana's cache in Elasticsearch
|
||||
* @param {dataSource} dataSource
|
||||
* @param {Function} callback A function to be executed with the results.
|
||||
*/
|
||||
mapper.getCachedFieldsFor = function (dataSource) {
|
||||
// If we already have the fields in the local cache, use those
|
||||
var cached = fieldCache.get(dataSource);
|
||||
if (cached) return Promise.resolved(cached);
|
||||
|
||||
return es.getSource({
|
||||
index: configFile.kibanaIndex,
|
||||
type: 'mapping',
|
||||
id: dataSource.get('index'),
|
||||
}).then(function (fields) {
|
||||
fieldCache.set(dataSource, fields);
|
||||
return fieldCache.get(dataSource);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an object containing all fields with their mappings directly from Elasticsearch _mapping API
|
||||
* @param {dataSource} dataSource
|
||||
* @param {Function} callback A function to be executed with the results.
|
||||
*/
|
||||
mapper.getFieldsFromEsFor = function (dataSource, callback) {
|
||||
return es.indices.getFieldMapping({
|
||||
// TODO: Change index to be the resolved in some way, last three months, last hour, last year, whatever
|
||||
index: dataSource.get('index'),
|
||||
field: '*',
|
||||
})
|
||||
.then(transformFieldMappingResponse)
|
||||
.then(function (fields) {
|
||||
return mapper.writeFieldsToCaches(dataSource, fields);
|
||||
})
|
||||
.then(function () {
|
||||
return fieldCache.get(dataSource);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores processed mappings in Elasticsearch, and in local cache
|
||||
* @param {dataSource} dataSource
|
||||
* @param {Function} callback A function to be executed with the results.
|
||||
* @async
|
||||
*/
|
||||
mapper.writeFieldsToCaches = function (dataSource, fields) {
|
||||
return es.index({
|
||||
index: configFile.kibanaIndex,
|
||||
type: 'mapping',
|
||||
id: dataSource.get('index'),
|
||||
body: fields
|
||||
})
|
||||
.then(function () {
|
||||
fieldCache.set(dataSource, fields);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears mapping caches from elasticsearch and from local object
|
||||
* @param {dataSource} dataSource
|
||||
* @param {Function} callback A function to be executed with the results.
|
||||
*/
|
||||
mapper.clearCache = function (dataSource) {
|
||||
fieldCache.clear(dataSource);
|
||||
return es.delete({
|
||||
index: configFile.kibanaIndex,
|
||||
type: 'mapping',
|
||||
id: dataSource.get('index')
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the ES response into the simple map for fields to
|
||||
* mappings which we will cache
|
||||
*
|
||||
* @param {object} response - complex, excessively nested
|
||||
* object returned from ES
|
||||
* @return {object} - simple object that works for all of kibana
|
||||
* use-cases
|
||||
*/
|
||||
var transformFieldMappingResponse = function (response) {
|
||||
var fields = _.cloneDeep(reservedFields);
|
||||
_.each(response, function (index, indexName) {
|
||||
if (indexName === configFile.kibanaIndex) return;
|
||||
_.each(index.mappings, function (mappings, typeName) {
|
||||
_.each(mappings, function (field, name) {
|
||||
if (_.size(field.mapping) === 0 || name[0] === '_') return;
|
||||
|
||||
var mapping = field.mapping[_.keys(field.mapping)[0]];
|
||||
mapping.type = castMappingType(mapping.type);
|
||||
|
||||
if (fields[name]) {
|
||||
if (fields[name].type !== mapping.type) {
|
||||
throw new MappingConflict(name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fields[name] = mapping;
|
||||
});
|
||||
});
|
||||
});
|
||||
return fields;
|
||||
};
|
||||
|
||||
/**
|
||||
* Accepts a mapping type, and converts it into it's js equivilent
|
||||
* @param {String} type - the type from the mapping's 'type' field
|
||||
* @return {String} - the most specific type that we care for
|
||||
*/
|
||||
var castMappingType = function (type) {
|
||||
switch (type) {
|
||||
case 'float':
|
||||
case 'double':
|
||||
case 'integer':
|
||||
case 'long':
|
||||
case 'short':
|
||||
case 'byte':
|
||||
case 'token_count':
|
||||
return 'number';
|
||||
case 'date':
|
||||
case 'boolean':
|
||||
case 'ip':
|
||||
case 'attachment':
|
||||
case 'geo_point':
|
||||
case 'geo_shape':
|
||||
return type;
|
||||
default: // including 'string'
|
||||
return 'string';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return CouriersMapper;
|
||||
});
|
||||
});
|
|
@ -1,11 +1,7 @@
|
|||
define(function () {
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* Create the mappingSetup module by passing in it's dependencies.
|
||||
*/
|
||||
return function (configFile, es, courier) {
|
||||
var mappingSetup = {};
|
||||
return function MappingSetupService(configFile, es) {
|
||||
var _ = require('lodash');
|
||||
var mappingSetup = this;
|
||||
|
||||
/**
|
||||
* Use to create the mappings, but that should only happen one at a time
|
||||
|
@ -81,8 +77,6 @@ define(function () {
|
|||
activeTypeCreations[type] = prom;
|
||||
return prom;
|
||||
};
|
||||
|
||||
return mappingSetup;
|
||||
};
|
||||
|
||||
});
|
24
src/kibana/components/courier/saved_object/_root_search.js
Normal file
24
src/kibana/components/courier/saved_object/_root_search.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
define(function (require) {
|
||||
return function RootSearchFactory(Private, config, $rootScope, timefilter) {
|
||||
return function makeRootSearch() {
|
||||
var SearchSource = Private(require('../data_source/search_source'));
|
||||
var indexPatterns = Private(require('../index_patterns/index_patterns'));
|
||||
|
||||
var source = (new SearchSource())
|
||||
.index(config.get('defaultIndex'))
|
||||
.filter(function (source) {
|
||||
return indexPatterns.get(source.get('index'))
|
||||
.then(function (indexPattern) {
|
||||
// dynamic time filter will be called in the _flatten phase of things
|
||||
return timefilter.get(indexPattern);
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('change:config.defaultIndex', function () {
|
||||
source.index(config.get('defaultIndex'));
|
||||
});
|
||||
|
||||
return source;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -1,12 +1,21 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('kibana/saved_object');
|
||||
var _ = require('lodash');
|
||||
|
||||
require('services/root_search');
|
||||
return function SavedObjectFactory(configFile, Promise, Private, Notifier) {
|
||||
var DocSource = Private(require('../data_source/doc_source'));
|
||||
var SearchSource = Private(require('../data_source/search_source'));
|
||||
|
||||
module.factory('SavedObject', function (courier, configFile, rootSearch, Promise, createNotifier, $injector) {
|
||||
var errors = Private(require('../_errors'));
|
||||
var mappingSetup = Private(require('./_mapping_setup'));
|
||||
|
||||
var mappingSetup = $injector.invoke(require('./_mapping_setup'));
|
||||
var json = {
|
||||
_serialize: function (val) {
|
||||
if (val != null) return JSON.stringify(val);
|
||||
},
|
||||
_deserialize: function (val) {
|
||||
if (val != null) return JSON.parse(val);
|
||||
}
|
||||
};
|
||||
|
||||
function SavedObject(config) {
|
||||
if (!_.isObject(config)) config = {};
|
||||
|
@ -18,18 +27,30 @@ define(function (require) {
|
|||
* Initialize config vars
|
||||
************/
|
||||
// the doc which is used to store this object
|
||||
var docSource = courier.createSource('doc');
|
||||
var docSource = new DocSource();
|
||||
|
||||
// type name for this object, used as the ES-type
|
||||
var type = config.type;
|
||||
|
||||
// Create a notifier for sending alerts
|
||||
var notify = createNotifier({
|
||||
var notify = new Notifier({
|
||||
location: 'Saved ' + type
|
||||
});
|
||||
|
||||
// mapping definition for the fields that this object will expose
|
||||
var mapping = config.mapping || {};
|
||||
var mapping = _.mapValues(config.mapping || {}, function (val, prop) {
|
||||
// allow shortcuts for the field types, by just setting the value
|
||||
// to the type name
|
||||
if (typeof val === 'string') val = { type: val };
|
||||
|
||||
if (val.type === 'json') {
|
||||
val.type = 'string';
|
||||
val._serialize = json._serialize;
|
||||
val._deserialize = json._deserialize;
|
||||
}
|
||||
|
||||
return val;
|
||||
});
|
||||
|
||||
// default field values, assigned when the source is loaded
|
||||
var defaults = config.defaults || {};
|
||||
|
@ -38,7 +59,7 @@ define(function (require) {
|
|||
var customInit = config.init || _.noop;
|
||||
|
||||
// optional search source which this object configures
|
||||
obj.searchSource = config.searchSource && courier.createSource('search');
|
||||
obj.searchSource = config.searchSource && new SearchSource();
|
||||
|
||||
// the id of the document
|
||||
obj.id = config.id || void 0;
|
||||
|
@ -51,8 +72,6 @@ define(function (require) {
|
|||
* @resolved {SavedObject}
|
||||
*/
|
||||
obj.init = _.once(function () {
|
||||
customInit();
|
||||
|
||||
// ensure that the type is defined
|
||||
if (!type) throw new Error('You must define a type name to use SavedObject objects.');
|
||||
|
||||
|
@ -64,7 +83,7 @@ define(function (require) {
|
|||
|
||||
// by default, the search source should inherit from the rootSearch
|
||||
if (obj.searchSource) {
|
||||
obj.searchSource.inherits(rootSearch);
|
||||
obj.searchSource.inherits(SavedObject.rootSearch());
|
||||
}
|
||||
|
||||
// check that the mapping for this type is defined
|
||||
|
@ -73,16 +92,6 @@ define(function (require) {
|
|||
// if it is already defined skip this step
|
||||
if (defined) return true;
|
||||
|
||||
// we need to setup the mapping, flesh it out first
|
||||
var mapping = _.mapValues(mapping, function (val, prop) {
|
||||
// allow shortcuts for the field types, by just setting the value
|
||||
// to the type name
|
||||
if (typeof val !== 'string') return val;
|
||||
return {
|
||||
type: val
|
||||
};
|
||||
});
|
||||
|
||||
mapping.kibanaSavedObjectMeta = {
|
||||
properties: {
|
||||
// setup the searchSource mapping, even if it is not used but this type yet
|
||||
|
@ -106,7 +115,7 @@ define(function (require) {
|
|||
// fetch the object from ES
|
||||
return docSource.fetch()
|
||||
.then(function applyESResp(resp) {
|
||||
if (!resp.found) throw new courier.errors.SavedObjectNotFound(type);
|
||||
if (!resp.found) throw new errors.SavedObjectNotFound(type);
|
||||
|
||||
var meta = resp._source.kibanaSavedObjectMeta || {};
|
||||
delete resp._source.kibanaSavedObjectMeta;
|
||||
|
@ -114,6 +123,13 @@ define(function (require) {
|
|||
// assign the defaults to the response
|
||||
_.defaults(resp._source, defaults);
|
||||
|
||||
// transform the source using _deserializers
|
||||
_.forOwn(mapping, function ittr(fieldMapping, fieldName) {
|
||||
if (fieldMapping._deserialize) {
|
||||
resp._source[fieldName] = fieldMapping._deserialize(resp._source[fieldName], resp, fieldName, fieldMapping);
|
||||
}
|
||||
});
|
||||
|
||||
// Give obj all of the values in _source.fields
|
||||
_.assign(obj, resp._source);
|
||||
|
||||
|
@ -133,6 +149,9 @@ define(function (require) {
|
|||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return customInit.call(obj);
|
||||
})
|
||||
.then(function () {
|
||||
// return our obj as the result of init()
|
||||
return obj;
|
||||
|
@ -151,7 +170,9 @@ define(function (require) {
|
|||
|
||||
_.forOwn(mapping, function (fieldMapping, fieldName) {
|
||||
if (obj[fieldName] != null) {
|
||||
body[fieldName] = obj[fieldName];
|
||||
body[fieldName] = (fieldMapping._serialize)
|
||||
? fieldMapping._serialize(obj[fieldName])
|
||||
: obj[fieldName];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -166,9 +187,11 @@ define(function (require) {
|
|||
|
||||
// index the document
|
||||
return docSource.doIndex(body).then(function (id) {
|
||||
// ensure that the object has the potentially new id
|
||||
obj.id = id;
|
||||
return id;
|
||||
})
|
||||
.then(function () {
|
||||
// ensure that the object has the potentially new id
|
||||
return obj.id;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -184,6 +207,12 @@ define(function (require) {
|
|||
|
||||
}
|
||||
|
||||
SavedObject.rootSearch = function () {
|
||||
var rootSearch = Private(require('./_root_search'))();
|
||||
SavedObject.rootSearch = function () { return rootSearch; };
|
||||
return rootSearch;
|
||||
};
|
||||
|
||||
return SavedObject;
|
||||
});
|
||||
};
|
||||
});
|
|
@ -4,7 +4,7 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
var modules = require('modules');
|
||||
var module = modules.get('kibana/notify');
|
||||
var errors = require('./errors');
|
||||
var errors = require('./_errors');
|
||||
var Notifier = require('./_notifier');
|
||||
var rootNotifier = new Notifier();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ define(function (require) {
|
|||
require('notify/notify');
|
||||
require('directives/info');
|
||||
require('angular-bootstrap');
|
||||
require('utils/private');
|
||||
|
||||
var modules = require('modules').get('kibana/controllers', ['ui.bootstrap']);
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('kibana/saved_object');
|
||||
var module = require('modules').get('kibana/directives');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.directive('savedObjectFinder', function (savedSearches, savedVisualizations, savedDashboards, indexPatterns, $parse) {
|
||||
module.directive('savedObjectFinder', function (savedSearches, savedVisualizations, savedDashboards) {
|
||||
var vars = {
|
||||
searches: {
|
||||
service: savedSearches,
|
||||
|
@ -16,11 +16,7 @@ define(function (require) {
|
|||
dashboards: {
|
||||
service: savedDashboards,
|
||||
noun: 'Dashboard'
|
||||
},
|
||||
indexPatterns: {
|
||||
service: indexPatterns,
|
||||
noun: 'Index Pattern'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -31,7 +27,7 @@ define(function (require) {
|
|||
makeUrl: '=?',
|
||||
onChoose: '=?'
|
||||
},
|
||||
template: require('text!./_finder.html'),
|
||||
template: require('text!../partials/saved_object_finder.html'),
|
||||
link: function ($scope, $el) {
|
||||
// the text input element
|
||||
var $input = $el.find('input[ng-model=filter]');
|
||||
|
@ -158,7 +154,9 @@ define(function (require) {
|
|||
};
|
||||
}()));
|
||||
|
||||
$scope.$on('$destroy', _.bindKey($input, 'off', 'keydown'));
|
||||
$scope.$on('$destroy', function () {
|
||||
$input.off('keydown');
|
||||
});
|
||||
|
||||
function filterResults() {
|
||||
if (!service) return;
|
|
@ -6,7 +6,6 @@ require.config({
|
|||
courier: './components/courier',
|
||||
config: './components/config',
|
||||
notify: './components/notify',
|
||||
saved_object: './components/saved_object',
|
||||
state_management: './components/state_management',
|
||||
|
||||
// special utils
|
||||
|
|
|
@ -49,7 +49,7 @@ define(function (require) {
|
|||
return defer.promise;
|
||||
}
|
||||
|
||||
Promise.all = $q.all;
|
||||
Promise.all = Promise.props = $q.all;
|
||||
Promise.resolved = function (val) {
|
||||
var defer = $q.defer();
|
||||
defer.resolve(val);
|
||||
|
@ -70,6 +70,11 @@ define(function (require) {
|
|||
Promise.map = function (arr, fn) {
|
||||
return Promise.all(arr.map(fn));
|
||||
};
|
||||
Promise.is = function (obj) {
|
||||
// $q doesn't create instances of any constructor, promises are just objects with a then function
|
||||
// https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335
|
||||
return obj && typeof obj.then === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a promise that uses our "event" like pattern.
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('kibana/services');
|
||||
|
||||
module.service('rootSearch', function (courier, config, timefilter, indexPatterns) {
|
||||
return courier.createSource('search')
|
||||
.index(config.get('defaultIndex'))
|
||||
.filter(function (source) {
|
||||
return source.getFields()
|
||||
.then(function (fields) {
|
||||
// dynamic time filter will be called in the _flatten phase of things
|
||||
return timefilter.get(fields);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -26,16 +26,16 @@ define(function (require) {
|
|||
return enable;
|
||||
};
|
||||
|
||||
this.get = function (fieldHash) {
|
||||
this.get = function (indexPattern) {
|
||||
var timefield, filter;
|
||||
|
||||
// TODO: time field should be stored in the pattern meta data. For now we just use the first date field we find
|
||||
timefield = _.findKey(fieldHash, {type: 'date'});
|
||||
timefield = _.find(indexPattern.fields, {type: 'date'});
|
||||
var bounds = this.getBounds();
|
||||
|
||||
if (!!timefield) {
|
||||
filter = {range : {}};
|
||||
filter.range[timefield] = {
|
||||
filter.range[timefield.name] = {
|
||||
gte: bounds.min,
|
||||
lte: bounds.max
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@import "../../bower_components/K4D3/k4.d3.css";
|
||||
@import url("//fonts.googleapis.com/css?family=Lato:400,700,400italic");
|
||||
@import "icon_font.css";
|
||||
/*!
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
@import "../../bower_components/font-awesome/less/font-awesome.less";
|
||||
@import "../../bower_components/K4D3/k4.d3.css";
|
||||
|
||||
// Custom variables
|
||||
@import "./_variables.less";
|
||||
|
|
|
@ -38,6 +38,13 @@ define(function (require) {
|
|||
});
|
||||
}(nestedObj));
|
||||
return flatObj;
|
||||
},
|
||||
// assign the properties of an object's subObject to the parent object.
|
||||
// obj = { prop: { a: 1} } ===> obj = { a: 1 }
|
||||
unwrapProp: function (obj, prop) {
|
||||
var wrapped = obj[prop];
|
||||
delete obj[prop];
|
||||
_.assign(obj, wrapped);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
35
src/kibana/utils/private.js
Normal file
35
src/kibana/utils/private.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
define(function (require) {
|
||||
|
||||
/**
|
||||
* create private services and factories that can still use angular deps
|
||||
* @type {[type]}
|
||||
*/
|
||||
var privPath = [];
|
||||
var pathToString = function () {
|
||||
return privPath.map(function (construct) {
|
||||
return construct.name;
|
||||
}).join(' -> ');
|
||||
};
|
||||
|
||||
var module = require('modules').get('kibana/utils');
|
||||
module.service('Private', function ($injector) {
|
||||
return function Private(construct) {
|
||||
if (typeof construct !== 'function') {
|
||||
throw new TypeError('Expected private module "' + construct + '" to be a function');
|
||||
}
|
||||
|
||||
var circular = !!(~privPath.indexOf(construct));
|
||||
privPath.push(construct);
|
||||
if (circular) throw new Error('Circluar private deps found: ' + pathToString());
|
||||
|
||||
if (!construct.$$instance) {
|
||||
var instance = {};
|
||||
construct.$$instance = $injector.invoke(construct, instance);
|
||||
construct.$$instance = construct.$$instance || instance;
|
||||
}
|
||||
|
||||
privPath.pop();
|
||||
return construct.$$instance;
|
||||
};
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
define(function (require) {
|
||||
var SearchSource = require('courier/data_source/search');
|
||||
var DocSource = require('courier/data_source/doc');
|
||||
var SearchSource = require('courier/data_source/search_source');
|
||||
var DocSource = require('courier/data_source/doc_source');
|
||||
|
||||
return function extendCourierSuite() {
|
||||
inject(function (courier) {
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
define(function (require) {
|
||||
require('angular-mocks');
|
||||
|
||||
describe('Courier Module', function () {
|
||||
var HastyRefresh;
|
||||
var courier;
|
||||
|
||||
before(function () {
|
||||
inject(function (couriersErrors, _courier_) {
|
||||
HastyRefresh = couriersErrors.HastyRefresh;
|
||||
courier = _courier_;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
courier.close();
|
||||
});
|
||||
|
||||
// describe('sync API', function () {
|
||||
// require('./create_source')();
|
||||
// require('./start_stop')();
|
||||
// require('./calculate_indices')();
|
||||
// require('./create_source')();
|
||||
// require('./abort')();
|
||||
// require('./on_fetch')();
|
||||
// require('./source_merging')();
|
||||
// });
|
||||
|
||||
// require('./data_source')();
|
||||
// require('./doc_source')();
|
||||
// require('./mapper')();
|
||||
});
|
||||
});
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
define(function (require) {
|
||||
var SearchSource = require('courier/data_source/search');
|
||||
var DocSource = require('courier/data_source/doc');
|
||||
var nextTick = require('utils/next_tick');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
|
||||
return function extendCourierSuite() {
|
||||
describe('onFetch()', function () {
|
||||
it('defers to the "fetch" method on the SearchSource class to do the fetch', function () {
|
||||
sinon.stub(SearchSource, 'fetch');
|
||||
|
||||
courier.fetch('search');
|
||||
expect(SearchSource.fetch.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('defers to the "validate" method on the DocSource class to determine which docs need fetching', function () {
|
||||
sinon.stub(DocSource, 'validate');
|
||||
|
||||
var courier = createCourier();
|
||||
|
||||
courier.fetch('doc');
|
||||
expect(DocSource.validate.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('when it receives refs from DocSource.validate, passes them back to DocSource.fetch', function (done) {
|
||||
sinon.stub(DocSource, 'validate', function (courier, refs, cb) {
|
||||
// just pass back the refs we receive
|
||||
nextTick(cb, null, refs);
|
||||
});
|
||||
sinon.spy(DocSource, 'fetch');
|
||||
|
||||
var courier = createCourier({
|
||||
client: stubbedClient(function (method, params, cb) {
|
||||
cb(null, {
|
||||
docs: [
|
||||
{
|
||||
found: true,
|
||||
_version: 1,
|
||||
_source: {}
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
courier
|
||||
.createSource('doc')
|
||||
.index('foo').type('bar').id('bax')
|
||||
.on('results', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
courier.fetch('doc');
|
||||
expect(DocSource.validate.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('calls it\'s own fetch method when the interval is up and immediately schedules another fetch', function () {
|
||||
var courier = createCourier();
|
||||
var clock = sinon.useFakeTimers();
|
||||
|
||||
var count = 0;
|
||||
sinon.stub(courier, 'fetch', function () {
|
||||
count++;
|
||||
});
|
||||
|
||||
courier.fetchInterval(10);
|
||||
courier.start();
|
||||
expect(count).to.eql(1);
|
||||
clock.tick(10);
|
||||
expect(count).to.eql(2);
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
*/
|
Loading…
Add table
Add a link
Reference in a new issue