mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Merge branch 'master' of github.com:elastic/kibana into upgrade/eslint-try2
This commit is contained in:
commit
1224b1829a
267 changed files with 4335 additions and 8842 deletions
|
@ -133,7 +133,7 @@ to open *Management/Kibana/Saved Objects/Dashboards*.
|
|||
[[sharing-dashboards]]
|
||||
== Sharing a Dashboard
|
||||
|
||||
You can can share a direct link to a Kibana dashboard with another user,
|
||||
You can either share a direct link to a Kibana dashboard with another user,
|
||||
or embed the dashboard in a web page. Users must have Kibana access
|
||||
to view embedded dashboards.
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ sophisticated date parsing APIs that Kibana uses to determine date information,
|
|||
specify dates in the index pattern name.
|
||||
+
|
||||
. Click *Create* to add the index pattern. This first pattern is automatically configured as the default.
|
||||
When you have more than one index pattern, you can designate which one to use as the default from
|
||||
*Settings > Indices*.
|
||||
When you have more than one index pattern, you can designate which one to use as the default by clicking
|
||||
on the star icon above the index pattern title from *Management > Index Patterns*.
|
||||
|
||||
All done! Kibana is now connected to your Elasticsearch data. Kibana displays a read-only list of fields
|
||||
configured for the matching index.
|
||||
|
|
|
@ -33,6 +33,7 @@ different series.
|
|||
instructions.
|
||||
<<metric-chart,Metric>>:: Display a single number.
|
||||
<<pie-chart,Pie chart>>:: Display each source's contribution to a total.
|
||||
<<tagcloud-chart,Tag cloud>>:: Display words as a cloud in which the size of the word correspond to its importance
|
||||
<<tilemap,Tile map>>:: Associate the results of an aggregation with geographic
|
||||
locations.
|
||||
Timeseries:: Compute and combine data from multiple time series
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[[tagcloud-chart]]
|
||||
== Cloud Tag Charts
|
||||
== Tag Clouds
|
||||
|
||||
A tag cloud visualization is a visual representation of text data, typically used to visualize free form text.
|
||||
Tags are usually single words, and the importance of each tag is shown with font size or color.
|
||||
|
|
|
@ -53,9 +53,7 @@ describe('plugins/elasticsearch', function () {
|
|||
expect(params.body.mappings.config.properties)
|
||||
.to.have.property('buildNum');
|
||||
expect(params.body.mappings.config.properties.buildNum)
|
||||
.to.have.property('type', 'string');
|
||||
expect(params.body.mappings.config.properties.buildNum)
|
||||
.to.have.property('index', 'not_analyzed');
|
||||
.to.have.property('type', 'keyword');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ export const mappings = {
|
|||
config: {
|
||||
properties: {
|
||||
buildNum: {
|
||||
type: 'string',
|
||||
index: 'not_analyzed'
|
||||
type: 'keyword'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -16,7 +16,6 @@ export default function HistogramVisType(Private) {
|
|||
'effect on the series above it.',
|
||||
params: {
|
||||
defaults: {
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
legendPosition: 'right',
|
||||
|
@ -27,8 +26,7 @@ export default function HistogramVisType(Private) {
|
|||
times: [],
|
||||
addTimeMarker: false,
|
||||
defaultYExtents: false,
|
||||
setYExtents: false,
|
||||
yAxis: {}
|
||||
setYExtents: false
|
||||
},
|
||||
legendPositions: [{
|
||||
value: 'left',
|
||||
|
|
|
@ -14,7 +14,6 @@ export default function HistogramVisType(Private) {
|
|||
'exact numbers or percentages. If you are not sure which chart you need, you could do worse than to start here.',
|
||||
params: {
|
||||
defaults: {
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
legendPosition: 'right',
|
||||
|
@ -23,8 +22,7 @@ export default function HistogramVisType(Private) {
|
|||
times: [],
|
||||
addTimeMarker: false,
|
||||
defaultYExtents: false,
|
||||
setYExtents: false,
|
||||
yAxis: {}
|
||||
setYExtents: false
|
||||
},
|
||||
legendPositions: [{
|
||||
value: 'left',
|
||||
|
|
|
@ -14,7 +14,6 @@ export default function HistogramVisType(Private) {
|
|||
'Be careful with sparse sets as the connection between points can be misleading.',
|
||||
params: {
|
||||
defaults: {
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
legendPosition: 'right',
|
||||
|
@ -27,8 +26,7 @@ export default function HistogramVisType(Private) {
|
|||
times: [],
|
||||
addTimeMarker: false,
|
||||
defaultYExtents: false,
|
||||
setYExtents: false,
|
||||
yAxis: {}
|
||||
setYExtents: false
|
||||
},
|
||||
legendPositions: [{
|
||||
value: 'left',
|
||||
|
|
|
@ -14,7 +14,6 @@ export default function HistogramVisType(Private) {
|
|||
'Pro Tip: Pie charts are best used sparingly, and with no more than 7 slices per pie.',
|
||||
params: {
|
||||
defaults: {
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
legendPosition: 'right',
|
||||
|
|
|
@ -2,12 +2,13 @@ import angular from 'angular';
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import 'plugins/kibana/dashboard/services/_saved_dashboard';
|
||||
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from '../components/panel/lib/panel_state';
|
||||
|
||||
describe('dashboard panels', function () {
|
||||
let $scope;
|
||||
let $el;
|
||||
|
||||
const compile = (dashboard) => {
|
||||
function compile(dashboard) {
|
||||
ngMock.inject(($rootScope, $controller, $compile, $route) => {
|
||||
$scope = $rootScope.$new();
|
||||
$route.current = {
|
||||
|
@ -19,12 +20,16 @@ describe('dashboard panels', function () {
|
|||
$el = angular.element(`
|
||||
<dashboard-app>
|
||||
<dashboard-grid style="width: 600px; height: 600px;"></dashboard-grid>
|
||||
</<dashboard-app>`);
|
||||
</dashboard-app>`);
|
||||
$compile($el)($scope);
|
||||
$scope.$digest();
|
||||
});
|
||||
};
|
||||
|
||||
function findPanelWithVisualizationId(id) {
|
||||
return $scope.state.panels.find((panel) => { return panel.id === id; });
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
ngMock.module('kibana');
|
||||
});
|
||||
|
@ -77,10 +82,30 @@ describe('dashboard panels', function () {
|
|||
compile(dash);
|
||||
});
|
||||
expect($scope.state.panels.length).to.be(16);
|
||||
const foo8Panel = $scope.state.panels.find(
|
||||
(panel) => { return panel.id === 'foo8'; });
|
||||
const foo8Panel = findPanelWithVisualizationId('foo8');
|
||||
expect(foo8Panel).to.not.be(null);
|
||||
expect(foo8Panel.row).to.be(8);
|
||||
expect(foo8Panel.col).to.be(1);
|
||||
});
|
||||
|
||||
it('initializes visualizations with the default size', function () {
|
||||
ngMock.inject((SavedDashboard) => {
|
||||
let dash = new SavedDashboard();
|
||||
dash.init();
|
||||
dash.panelsJSON = `[
|
||||
{"col":3,"id":"foo1","row":1,"type":"visualization"},
|
||||
{"col":5,"id":"foo2","row":1,"size_x":5,"size_y":9,"type":"visualization"}]`;
|
||||
compile(dash);
|
||||
});
|
||||
expect($scope.state.panels.length).to.be(2);
|
||||
const foo1Panel = findPanelWithVisualizationId('foo1');
|
||||
expect(foo1Panel).to.not.be(null);
|
||||
expect(foo1Panel.size_x).to.be(DEFAULT_PANEL_WIDTH);
|
||||
expect(foo1Panel.size_y).to.be(DEFAULT_PANEL_HEIGHT);
|
||||
|
||||
const foo2Panel = findPanelWithVisualizationId('foo2');
|
||||
expect(foo2Panel).to.not.be(null);
|
||||
expect(foo2Panel.size_x).to.be(5);
|
||||
expect(foo2Panel.size_y).to.be(9);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
export const DEFAULT_PANEL_WIDTH = 3;
|
||||
export const DEFAULT_PANEL_HEIGHT = 2;
|
||||
|
||||
/**
|
||||
* Represents a panel on a grid. Keeps track of position in the grid and what visualization it
|
||||
* contains.
|
||||
*
|
||||
* @typedef {Object} PanelState
|
||||
* @property {number} id - Id of the visualization contained in the panel.
|
||||
* @property {Element} $el - A reference to the gridster widget holding this panel. Used to
|
||||
* update the size and column attributes. TODO: move out of panel state as this couples state to ui.
|
||||
* @property {string} type - Type of the visualization in the panel.
|
||||
* @property {number} panelId - Unique id to represent this panel in the grid.
|
||||
* @property {number} size_x - Width of the panel.
|
||||
* @property {number} size_y - Height of the panel.
|
||||
* @property {number} col - Column index in the grid.
|
||||
* @property {number} row - Row index in the grid.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates and initializes a basic panel state.
|
||||
* @param {number} id
|
||||
* @param {string} type
|
||||
* @param {number} panelId
|
||||
* @return {PanelState}
|
||||
*/
|
||||
export function createPanelState(id, type, panelId) {
|
||||
return {
|
||||
size_x: DEFAULT_PANEL_WIDTH,
|
||||
size_y: DEFAULT_PANEL_HEIGHT,
|
||||
panelId: panelId,
|
||||
type: type,
|
||||
id: id
|
||||
};
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from 'plugins/kibana/dashboard/components/panel/lib/panel_state';
|
||||
|
||||
export class PanelUtils {
|
||||
/**
|
||||
* Fills in default parameters where not specified.
|
||||
* @param {PanelState} panel
|
||||
*/
|
||||
static initializeDefaults(panel) {
|
||||
panel.size_x = panel.size_x || DEFAULT_PANEL_WIDTH;
|
||||
panel.size_y = panel.size_y || DEFAULT_PANEL_HEIGHT;
|
||||
|
||||
if (!panel.id) {
|
||||
// In the interest of backwards comparability
|
||||
if (panel.visId) {
|
||||
panel.id = panel.visId;
|
||||
panel.type = 'visualization';
|
||||
delete panel.visId;
|
||||
} else {
|
||||
throw new Error('Missing object id on panel');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the panel object has the latest size/pos info.
|
||||
* @param {PanelState} panel
|
||||
*/
|
||||
static refreshSizeAndPosition(panel) {
|
||||
const data = panel.$el.coords().grid;
|
||||
panel.size_x = data.size_x;
|
||||
panel.size_y = data.size_y;
|
||||
panel.col = data.col;
|
||||
panel.row = data.row;
|
||||
}
|
||||
|
||||
/**
|
||||
* $el is a circular structure because it contains a reference to it's parent panel,
|
||||
* so it needs to be removed before it can be serialized (we also don't
|
||||
* want it to show up in the url).
|
||||
* @param {PanelState} panel
|
||||
*/
|
||||
static makeSerializeable(panel) {
|
||||
delete panel.$el;
|
||||
}
|
||||
}
|
|
@ -4,13 +4,13 @@
|
|||
{{::savedObj.title}}
|
||||
</span>
|
||||
<div class="btn-group">
|
||||
<a aria-label="Edit" ng-show="chrome.getVisible() && editUrl" ng-href="{{::editUrl}}">
|
||||
<a aria-label="Edit" ng-show="!isFullScreenMode && editUrl" ng-href="{{::editUrl}}">
|
||||
<i aria-hidden="true" class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a aria-label="Move" ng-show="chrome.getVisible()" class="panel-move">
|
||||
<a aria-label="Move" ng-show="!isFullScreenMode" class="panel-move">
|
||||
<i aria-hidden="true" class="fa fa-arrows"></i>
|
||||
</a>
|
||||
<a aria-label="Remove" ng-show="chrome.getVisible()" ng-click="remove()">
|
||||
<a aria-label="Remove" ng-show="!isFullScreenMode" ng-click="remove()">
|
||||
<i aria-hidden="true" class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -26,7 +26,7 @@
|
|||
ng-switch-when="visualization"
|
||||
vis="savedObj.vis"
|
||||
search-source="savedObj.searchSource"
|
||||
show-spy-panel="chrome.getVisible()"
|
||||
show-spy-panel="!isFullScreenMode"
|
||||
ui-state="uiState"
|
||||
render-counter
|
||||
class="panel-content">
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import 'ui/visualize';
|
||||
import 'ui/doc_table';
|
||||
import { loadPanelProvider } from 'plugins/kibana/dashboard/components/panel/lib/load_panel';
|
||||
import FilterManagerProvider from 'ui/filter_manager';
|
||||
import uiModules from 'ui/modules';
|
||||
import panelTemplate from 'plugins/kibana/dashboard/components/panel/panel.html';
|
||||
|
||||
uiModules
|
||||
.get('app/dashboard')
|
||||
.directive('dashboardPanel', function (savedVisualizations, savedSearches, Notifier, Private, $injector) {
|
||||
const loadPanel = Private(loadPanelProvider);
|
||||
const filterManager = Private(FilterManagerProvider);
|
||||
|
||||
const services = require('plugins/kibana/management/saved_object_registry').all().map(function (serviceObj) {
|
||||
const service = $injector.get(serviceObj.service);
|
||||
return {
|
||||
type: service.type,
|
||||
name: serviceObj.service
|
||||
};
|
||||
});
|
||||
|
||||
const getPanelId = function (panel) {
|
||||
return ['P', panel.panelIndex].join('-');
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: panelTemplate,
|
||||
link: function ($scope) {
|
||||
// using $scope inheritance, panels are available in AppState
|
||||
const $state = $scope.state;
|
||||
|
||||
// receives $scope.panel from the dashboard grid directive, seems like should be isolate?
|
||||
$scope.$watch('id', function () {
|
||||
if (!$scope.panel.id || !$scope.panel.type) return;
|
||||
|
||||
loadPanel($scope.panel, $scope)
|
||||
.then(function (panelConfig) {
|
||||
// These could be done in loadPanel, putting them here to make them more explicit
|
||||
$scope.savedObj = panelConfig.savedObj;
|
||||
$scope.editUrl = panelConfig.editUrl;
|
||||
$scope.$on('$destroy', function () {
|
||||
panelConfig.savedObj.destroy();
|
||||
$scope.parentUiState.removeChild(getPanelId(panelConfig.panel));
|
||||
});
|
||||
|
||||
// create child ui state from the savedObj
|
||||
const uiState = panelConfig.uiState || {};
|
||||
$scope.uiState = $scope.parentUiState.createChild(getPanelId(panelConfig.panel), uiState, true);
|
||||
const panelSavedVis = _.get(panelConfig, 'savedObj.vis'); // Sometimes this will be a search, and undef
|
||||
if (panelSavedVis) {
|
||||
panelSavedVis.setUiState($scope.uiState);
|
||||
}
|
||||
|
||||
$scope.filter = function (field, value, operator) {
|
||||
const index = $scope.savedObj.searchSource.get('index').id;
|
||||
filterManager.add(field, value, operator, index);
|
||||
};
|
||||
})
|
||||
.catch(function (e) {
|
||||
$scope.error = e.message;
|
||||
|
||||
// If the savedObjectType matches the panel type, this means the object itself has been deleted,
|
||||
// so we shouldn't even have an edit link. If they don't match, it means something else is wrong
|
||||
// with the object (but the object still exists), so we link to the object editor instead.
|
||||
const objectItselfDeleted = e.savedObjectType === $scope.panel.type;
|
||||
if (objectItselfDeleted) return;
|
||||
|
||||
const type = $scope.panel.type;
|
||||
const id = $scope.panel.id;
|
||||
const service = _.find(services, { type: type });
|
||||
if (!service) return;
|
||||
|
||||
$scope.editUrl = '#management/kibana/objects/' + service.name + '/' + id + '?notFound=' + e.savedObjectType;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$scope.remove = function () {
|
||||
_.pull($state.panels, $scope.panel);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
import _ from 'lodash';
|
||||
import 'ui/visualize';
|
||||
import 'ui/doc_table';
|
||||
import { loadPanelProvider } from 'plugins/kibana/dashboard/components/panel/lib/load_panel';
|
||||
import FilterManagerProvider from 'ui/filter_manager';
|
||||
import uiModules from 'ui/modules';
|
||||
import panelTemplate from 'plugins/kibana/dashboard/components/panel/panel.html';
|
||||
|
||||
uiModules
|
||||
.get('app/dashboard')
|
||||
.directive('dashboardPanel', function (savedVisualizations, savedSearches, Notifier, Private, $injector) {
|
||||
const loadPanel = Private(loadPanelProvider);
|
||||
const filterManager = Private(FilterManagerProvider);
|
||||
|
||||
const services = require('plugins/kibana/management/saved_object_registry').all().map(function (serviceObj) {
|
||||
const service = $injector.get(serviceObj.service);
|
||||
return {
|
||||
type: service.type,
|
||||
name: serviceObj.service
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a unique id for storing the panel state in the persistent ui.
|
||||
* @param {PanelState} panel
|
||||
* @returns {string}
|
||||
*/
|
||||
const getPersistedStateId = function (panel) {
|
||||
return `P-${panel.panelId}`;
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: panelTemplate,
|
||||
scope: {
|
||||
/**
|
||||
* Whether or not the dashboard this panel is contained on is in 'full screen mode'.
|
||||
* @type {boolean}
|
||||
*/
|
||||
isFullScreenMode: '=',
|
||||
/**
|
||||
* The parent's persisted state is used to create a child persisted state for the
|
||||
* panel.
|
||||
* @type {PersistedState}
|
||||
*/
|
||||
parentUiState: '=',
|
||||
/**
|
||||
* Contains information about this panel.
|
||||
* @type {PanelState}
|
||||
*/
|
||||
panel: '=',
|
||||
/**
|
||||
* Handles removing this panel from the grid.
|
||||
* @type {() => void}
|
||||
*/
|
||||
remove: '&'
|
||||
},
|
||||
link: function ($scope, element) {
|
||||
if (!$scope.panel.id || !$scope.panel.type) return;
|
||||
|
||||
loadPanel($scope.panel, $scope)
|
||||
.then(function (panelConfig) {
|
||||
// These could be done in loadPanel, putting them here to make them more explicit
|
||||
$scope.savedObj = panelConfig.savedObj;
|
||||
$scope.editUrl = panelConfig.editUrl;
|
||||
|
||||
element.on('$destroy', function () {
|
||||
panelConfig.savedObj.destroy();
|
||||
$scope.parentUiState.removeChild(getPersistedStateId(panelConfig.panel));
|
||||
$scope.$destroy();
|
||||
});
|
||||
|
||||
// create child ui state from the savedObj
|
||||
const uiState = panelConfig.uiState || {};
|
||||
$scope.uiState = $scope.parentUiState.createChild(getPersistedStateId(panelConfig.panel), uiState, true);
|
||||
const panelSavedVis = _.get(panelConfig, 'savedObj.vis'); // Sometimes this will be a search, and undef
|
||||
if (panelSavedVis) {
|
||||
panelSavedVis.setUiState($scope.uiState);
|
||||
}
|
||||
|
||||
$scope.filter = function (field, value, operator) {
|
||||
const index = $scope.savedObj.searchSource.get('index').id;
|
||||
filterManager.add(field, value, operator, index);
|
||||
};
|
||||
})
|
||||
.catch(function (e) {
|
||||
$scope.error = e.message;
|
||||
|
||||
// If the savedObjectType matches the panel type, this means the object itself has been deleted,
|
||||
// so we shouldn't even have an edit link. If they don't match, it means something else is wrong
|
||||
// with the object (but the object still exists), so we link to the object editor instead.
|
||||
const objectItselfDeleted = e.savedObjectType === $scope.panel.type;
|
||||
if (objectItselfDeleted) return;
|
||||
|
||||
const type = $scope.panel.type;
|
||||
const id = $scope.panel.id;
|
||||
const service = _.find(services, { type: type });
|
||||
if (!service) return;
|
||||
|
||||
$scope.editUrl = '#management/kibana/objects/' + service.name + '/' + id + '?notFound=' + e.savedObjectType;
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -3,6 +3,7 @@ import $ from 'jquery';
|
|||
import Binder from 'ui/binder';
|
||||
import 'gridster';
|
||||
import uiModules from 'ui/modules';
|
||||
import { PanelUtils } from 'plugins/kibana/dashboard/components/panel/lib/panel_utils';
|
||||
|
||||
const app = uiModules.get('app/dashboard');
|
||||
|
||||
|
@ -33,6 +34,24 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
|
|||
// debounced layout function is safe to call as much as possible
|
||||
const safeLayout = _.debounce(layout, 200);
|
||||
|
||||
$scope.removePanelFromState = (panelId) => {
|
||||
_.remove($scope.state.panels, function (panel) {
|
||||
return panel.panelId === panelId;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the panel with the given id from the $scope.state.panels array. Does not
|
||||
* remove the ui element from gridster - that is triggered by a watcher that is
|
||||
* triggered on changes made to $scope.state.panels.
|
||||
* @param panelId {number}
|
||||
*/
|
||||
$scope.getPanelByPanelId = (panelId) => {
|
||||
return _.find($scope.state.panels, function (panel) {
|
||||
return panel.panelId === panelId;
|
||||
});
|
||||
};
|
||||
|
||||
function init() {
|
||||
$el.addClass('gridster');
|
||||
|
||||
|
@ -90,7 +109,7 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
|
|||
};
|
||||
|
||||
// ensure that every panel can be serialized now that we are done
|
||||
$state.panels.forEach(makePanelSerializeable);
|
||||
$state.panels.forEach(PanelUtils.makeSerializeable);
|
||||
|
||||
// alert interested parties that we have finished processing changes to the panels
|
||||
// TODO: change this from event based to calling a method on dashboardApp
|
||||
|
@ -108,7 +127,7 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
|
|||
panel.$el.stop();
|
||||
removePanel(panel, true);
|
||||
// not that we will, but lets be safe
|
||||
makePanelSerializeable(panel);
|
||||
PanelUtils.makeSerializeable(panel);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -121,81 +140,44 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
|
|||
// return the panel object for an element.
|
||||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// ALWAYS CALL makePanelSerializeable AFTER YOU ARE DONE WITH IT
|
||||
// ALWAYS CALL PanelUtils.makeSerializeable AFTER YOU ARE DONE WITH IT
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
function getPanelFor(el) {
|
||||
const $panel = el.jquery ? el : $(el);
|
||||
const panel = $panel.data('panel');
|
||||
|
||||
panel.$el = $panel;
|
||||
panel.$scope = $panel.data('$scope');
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
// since the $el and $scope are circular structures, they need to be
|
||||
// removed from panel before it can be serialized (we also wouldn't
|
||||
// want them to show up in the url)
|
||||
function makePanelSerializeable(panel) {
|
||||
delete panel.$el;
|
||||
delete panel.$scope;
|
||||
}
|
||||
|
||||
// tell gridster to remove the panel, and cleanup our metadata
|
||||
function removePanel(panel, silent) {
|
||||
// remove from grister 'silently' (don't reorganize after)
|
||||
gridster.remove_widget(panel.$el, silent);
|
||||
|
||||
// destroy the scope
|
||||
panel.$scope.$destroy();
|
||||
|
||||
panel.$el.removeData('panel');
|
||||
panel.$el.removeData('$scope');
|
||||
}
|
||||
|
||||
// tell gridster to add the panel, and create additional meatadata like $scope
|
||||
function addPanel(panel) {
|
||||
_.defaults(panel, {
|
||||
size_x: 3,
|
||||
size_y: 2
|
||||
});
|
||||
PanelUtils.initializeDefaults(panel);
|
||||
|
||||
// ignore panels that don't have vis id's
|
||||
if (!panel.id) {
|
||||
// In the interest of backwards compat
|
||||
if (panel.visId) {
|
||||
panel.id = panel.visId;
|
||||
panel.type = 'visualization';
|
||||
delete panel.visId;
|
||||
} else {
|
||||
throw new Error('missing object id on panel');
|
||||
}
|
||||
}
|
||||
|
||||
panel.$scope = $scope.$new();
|
||||
panel.$scope.panel = panel;
|
||||
panel.$scope.parentUiState = $scope.uiState;
|
||||
|
||||
panel.$el = $compile('<li><dashboard-panel></li>')(panel.$scope);
|
||||
const panelHtml = `
|
||||
<li>
|
||||
<dashboard-panel remove="removePanelFromState(${panel.panelId})"
|
||||
panel="getPanelByPanelId(${panel.panelId})"
|
||||
is-full-screen-mode="!chrome.getVisible()"
|
||||
parent-ui-state="uiState">
|
||||
</li>`;
|
||||
panel.$el = $compile(panelHtml)($scope);
|
||||
|
||||
// tell gridster to use the widget
|
||||
gridster.add_widget(panel.$el, panel.size_x, panel.size_y, panel.col, panel.row);
|
||||
|
||||
// update size/col/etc.
|
||||
refreshPanelStats(panel);
|
||||
// Gridster may change the position of the widget when adding it, make sure the panel
|
||||
// contains the latest info.
|
||||
PanelUtils.refreshSizeAndPosition(panel);
|
||||
|
||||
// stash the panel and it's scope in the element's data
|
||||
// stash the panel in the element's data
|
||||
panel.$el.data('panel', panel);
|
||||
panel.$el.data('$scope', panel.$scope);
|
||||
}
|
||||
|
||||
// ensure that the panel object has the latest size/pos info
|
||||
function refreshPanelStats(panel) {
|
||||
const data = panel.$el.coords().grid;
|
||||
panel.size_x = data.size_x;
|
||||
panel.size_y = data.size_y;
|
||||
panel.col = data.col;
|
||||
panel.row = data.row;
|
||||
}
|
||||
|
||||
// when gridster tell us it made a change, update each of the panel objects
|
||||
|
@ -203,9 +185,8 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
|
|||
// ensure that our panel objects keep their size in sync
|
||||
gridster.$widgets.each(function (i, el) {
|
||||
const panel = getPanelFor(el);
|
||||
refreshPanelStats(panel);
|
||||
panel.$scope.$broadcast('resize');
|
||||
makePanelSerializeable(panel);
|
||||
PanelUtils.refreshSizeAndPosition(panel);
|
||||
PanelUtils.makeSerializeable(panel);
|
||||
$scope.$root.$broadcast('change:vis');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'ui/notify';
|
|||
import 'ui/typeahead';
|
||||
import 'ui/share';
|
||||
import 'plugins/kibana/dashboard/directives/grid';
|
||||
import 'plugins/kibana/dashboard/components/panel/panel';
|
||||
import 'plugins/kibana/dashboard/directives/dashboard_panel';
|
||||
import 'plugins/kibana/dashboard/services/saved_dashboards';
|
||||
import 'plugins/kibana/dashboard/styles/main.less';
|
||||
import FilterBarQueryFilterProvider from 'ui/filter_bar/query_filter';
|
||||
|
@ -17,6 +17,7 @@ import uiRoutes from 'ui/routes';
|
|||
import uiModules from 'ui/modules';
|
||||
import indexTemplate from 'plugins/kibana/dashboard/index.html';
|
||||
import { savedDashboardRegister } from 'plugins/kibana/dashboard/services/saved_dashboard_register';
|
||||
import { createPanelState } from 'plugins/kibana/dashboard/components/panel/lib/panel_state';
|
||||
require('ui/saved_objects/saved_object_registry').register(savedDashboardRegister);
|
||||
|
||||
const app = uiModules.get('app/dashboard', [
|
||||
|
@ -152,7 +153,7 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
|
|||
docTitle.change(dash.title);
|
||||
}
|
||||
|
||||
initPanelIndices();
|
||||
initPanelIds();
|
||||
|
||||
// watch for state changes and update the appStatus.dirty value
|
||||
stateMonitor = stateMonitorFactory.create($state, stateDefaults);
|
||||
|
@ -171,24 +172,23 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
|
|||
$scope.$emit('application.load');
|
||||
}
|
||||
|
||||
function initPanelIndices() {
|
||||
// find the largest panelIndex in all the panels
|
||||
let maxIndex = getMaxPanelIndex();
|
||||
function initPanelIds() {
|
||||
// find the largest panelId in all the panels
|
||||
let maxIndex = getMaxPanelId();
|
||||
|
||||
// ensure that all panels have a panelIndex
|
||||
// ensure that all panels have a panelId
|
||||
$scope.state.panels.forEach(function (panel) {
|
||||
if (!panel.panelIndex) {
|
||||
panel.panelIndex = maxIndex++;
|
||||
if (!panel.panelId) {
|
||||
panel.panelId = maxIndex++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getMaxPanelIndex() {
|
||||
let index = $scope.state.panels.reduce(function (idx, panel) {
|
||||
// if panel is missing an index, add one and increment the index
|
||||
return Math.max(idx, panel.panelIndex || idx);
|
||||
function getMaxPanelId() {
|
||||
let maxId = $scope.state.panels.reduce(function (id, panel) {
|
||||
return Math.max(id, panel.panelId || id);
|
||||
}, 0);
|
||||
return ++index;
|
||||
return ++maxId;
|
||||
}
|
||||
|
||||
function updateQueryOnRootSource() {
|
||||
|
@ -272,12 +272,12 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
|
|||
// called by the saved-object-finder when a user clicks a vis
|
||||
$scope.addVis = function (hit) {
|
||||
pendingVis++;
|
||||
$state.panels.push({ id: hit.id, type: 'visualization', panelIndex: getMaxPanelIndex() });
|
||||
$state.panels.push(createPanelState(hit.id, 'visualization', getMaxPanelId()));
|
||||
};
|
||||
|
||||
$scope.addSearch = function (hit) {
|
||||
pendingVis++;
|
||||
$state.panels.push({ id: hit.id, type: 'search', panelIndex: getMaxPanelIndex() });
|
||||
$state.panels.push(createPanelState(hit.id, 'search', getMaxPanelId()));
|
||||
};
|
||||
|
||||
// Setup configurable values for config directive, after objects are initialized
|
||||
|
|
|
@ -46,20 +46,20 @@ module.factory('SavedDashboard', function (courier, config) {
|
|||
|
||||
// if type:dashboard has no mapping, we push this mapping into ES
|
||||
SavedDashboard.mapping = {
|
||||
title: 'string',
|
||||
title: 'text',
|
||||
hits: 'integer',
|
||||
description: 'string',
|
||||
panelsJSON: 'string',
|
||||
optionsJSON: 'string',
|
||||
uiStateJSON: 'string',
|
||||
description: 'text',
|
||||
panelsJSON: 'text',
|
||||
optionsJSON: 'text',
|
||||
uiStateJSON: 'text',
|
||||
version: 'integer',
|
||||
timeRestore: 'boolean',
|
||||
timeTo: 'string',
|
||||
timeFrom: 'string',
|
||||
timeTo: 'keyword',
|
||||
timeFrom: 'keyword',
|
||||
refreshInterval: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
display: {type: 'string'},
|
||||
display: {type: 'keyword'},
|
||||
pause: { type: 'boolean'},
|
||||
section: { type: 'integer'},
|
||||
value: { type: 'integer'}
|
||||
|
|
|
@ -31,11 +31,11 @@ module.factory('SavedSearch', function (courier) {
|
|||
SavedSearch.type = 'search';
|
||||
|
||||
SavedSearch.mapping = {
|
||||
title: 'string',
|
||||
description: 'string',
|
||||
title: 'text',
|
||||
description: 'text',
|
||||
hits: 'integer',
|
||||
columns: 'string',
|
||||
sort: 'string',
|
||||
columns: 'keyword',
|
||||
sort: 'keyword',
|
||||
version: 'integer'
|
||||
};
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import jsondiffpatch from '@bigfunger/jsondiffpatch';
|
||||
import '../styles/_output_preview.less';
|
||||
import outputPreviewTemplate from '../views/output_preview.html';
|
||||
|
||||
const htmlFormat = jsondiffpatch.formatters.html.format;
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('outputPreview', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: outputPreviewTemplate,
|
||||
scope: {
|
||||
oldObject: '=',
|
||||
newObject: '=',
|
||||
error: '='
|
||||
},
|
||||
link: function ($scope, $el) {
|
||||
const div = $el.find('.visual')[0];
|
||||
|
||||
$scope.diffpatch = jsondiffpatch.create({
|
||||
arrays: {
|
||||
detectMove: false
|
||||
},
|
||||
textDiff: {
|
||||
minLength: 120
|
||||
}
|
||||
});
|
||||
|
||||
$scope.updateUi = function () {
|
||||
let left = $scope.oldObject;
|
||||
let right = $scope.newObject;
|
||||
let delta = $scope.diffpatch.diff(left, right);
|
||||
if (!delta || $scope.error) delta = {};
|
||||
|
||||
div.innerHTML = htmlFormat(delta, left);
|
||||
};
|
||||
},
|
||||
controller: function ($scope, debounce) {
|
||||
$scope.collapsed = false;
|
||||
|
||||
const updateOutput = debounce(function () {
|
||||
$scope.updateUi();
|
||||
}, 200);
|
||||
|
||||
$scope.$watch('oldObject', updateOutput);
|
||||
$scope.$watch('newObject', updateOutput);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import '../styles/_pipeline_output.less';
|
||||
import pipelineOutputTemplate from '../views/pipeline_output.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('pipelineOutput', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: pipelineOutputTemplate,
|
||||
scope: {
|
||||
pipeline: '=',
|
||||
samples: '=',
|
||||
sample: '='
|
||||
},
|
||||
controller: function ($scope) {
|
||||
$scope.collapsed = true;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,94 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import _ from 'lodash';
|
||||
import Pipeline from '../lib/pipeline';
|
||||
import angular from 'angular';
|
||||
import * as ProcessorTypes from '../processors/view_models';
|
||||
import IngestProvider from 'ui/ingest';
|
||||
import '../styles/_pipeline_setup.less';
|
||||
import './pipeline_output';
|
||||
import './source_data';
|
||||
import './processor_ui_container';
|
||||
import '../processors';
|
||||
import pipelineSetupTemplate from '../views/pipeline_setup.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
function buildProcessorTypeList(enabledProcessorTypeIds) {
|
||||
return _(ProcessorTypes)
|
||||
.map(Type => {
|
||||
const instance = new Type();
|
||||
return {
|
||||
typeId: instance.typeId,
|
||||
title: instance.title,
|
||||
Type
|
||||
};
|
||||
})
|
||||
.compact()
|
||||
.filter((processorType) => enabledProcessorTypeIds.includes(processorType.typeId))
|
||||
.sortBy('title')
|
||||
.value();
|
||||
}
|
||||
|
||||
app.directive('pipelineSetup', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: pipelineSetupTemplate,
|
||||
scope: {
|
||||
samples: '=',
|
||||
pipeline: '='
|
||||
},
|
||||
controller: function ($scope, debounce, Private, Notifier) {
|
||||
const ingest = Private(IngestProvider);
|
||||
const notify = new Notifier({ location: `Ingest Pipeline Setup` });
|
||||
$scope.sample = {};
|
||||
|
||||
//determines which processors are available on the cluster
|
||||
ingest.getProcessors()
|
||||
.then((enabledProcessorTypeIds) => {
|
||||
$scope.processorTypes = buildProcessorTypeList(enabledProcessorTypeIds);
|
||||
})
|
||||
.catch(notify.error);
|
||||
|
||||
const pipeline = new Pipeline();
|
||||
// Loads pre-existing pipeline which will exist if the user returns from
|
||||
// a later step in the wizard
|
||||
if ($scope.pipeline) {
|
||||
pipeline.load($scope.pipeline);
|
||||
$scope.sample = $scope.pipeline.input;
|
||||
}
|
||||
$scope.pipeline = pipeline;
|
||||
|
||||
//initiates the simulate call if the pipeline is dirty
|
||||
const simulatePipeline = debounce((event, message) => {
|
||||
if (pipeline.processors.length === 0) {
|
||||
pipeline.updateOutput();
|
||||
return;
|
||||
}
|
||||
|
||||
return ingest.simulate(pipeline.model)
|
||||
.then((results) => { pipeline.applySimulateResults(results); })
|
||||
.catch(notify.error);
|
||||
}, 200);
|
||||
|
||||
$scope.$watchCollection('pipeline.processors', (newVal, oldVal) => {
|
||||
pipeline.updateParents();
|
||||
});
|
||||
|
||||
$scope.$watch('sample', (newVal) => {
|
||||
pipeline.input = $scope.sample;
|
||||
pipeline.updateParents();
|
||||
});
|
||||
|
||||
$scope.$watch('processorType', (newVal) => {
|
||||
if (!newVal) return;
|
||||
|
||||
pipeline.add(newVal.Type);
|
||||
$scope.processorType = '';
|
||||
});
|
||||
|
||||
$scope.$watch('pipeline.dirty', simulatePipeline);
|
||||
|
||||
$scope.expandContext = 1;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import _ from 'lodash';
|
||||
import '../styles/_processor_ui_container.less';
|
||||
import './output_preview';
|
||||
import './processor_ui_container_header';
|
||||
import template from '../views/processor_ui_container.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('processorUiContainer', function ($compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
pipeline: '=',
|
||||
processor: '='
|
||||
},
|
||||
template: template,
|
||||
link: function ($scope, $el) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
const $container = $el.find('.processor-ui-content');
|
||||
const typeId = processor.typeId;
|
||||
|
||||
const newScope = $scope.$new();
|
||||
newScope.pipeline = pipeline;
|
||||
newScope.processor = processor;
|
||||
|
||||
const template = `<processor-ui-${typeId}></processor-ui-${typeId}>`;
|
||||
const $innerEl = $compile(template)(newScope);
|
||||
|
||||
$innerEl.appendTo($container);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import '../styles/_processor_ui_container_header.less';
|
||||
import processorUiContainerHeaderTemplate from '../views/processor_ui_container_header.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('processorUiContainerHeader', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
processor: '=',
|
||||
field: '=',
|
||||
pipeline: '='
|
||||
},
|
||||
template: processorUiContainerHeaderTemplate
|
||||
};
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import angular from 'angular';
|
||||
import '../styles/_source_data.less';
|
||||
import sourceDataTemplate from '../views/source_data.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('sourceData', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
samples: '=',
|
||||
sample: '=',
|
||||
disabled: '='
|
||||
},
|
||||
template: sourceDataTemplate,
|
||||
controller: function ($scope) {
|
||||
const samples = $scope.samples;
|
||||
|
||||
if (samples.length > 0) {
|
||||
$scope.selectedSample = samples[0];
|
||||
}
|
||||
|
||||
$scope.$watch('selectedSample', (newValue) => {
|
||||
//the added complexity of this directive is to strip out the properties
|
||||
//that angular adds to array objects that are bound via ng-options
|
||||
$scope.sample = angular.copy(newValue);
|
||||
});
|
||||
|
||||
$scope.previousLine = function () {
|
||||
let currentIndex = samples.indexOf($scope.selectedSample);
|
||||
if (currentIndex <= 0) currentIndex = samples.length;
|
||||
|
||||
$scope.selectedSample = samples[currentIndex - 1];
|
||||
};
|
||||
|
||||
$scope.nextLine = function () {
|
||||
let currentIndex = samples.indexOf($scope.selectedSample);
|
||||
if (currentIndex >= samples.length - 1) currentIndex = -1;
|
||||
|
||||
$scope.selectedSample = samples[currentIndex + 1];
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
import './directives/pipeline_setup';
|
|
@ -1,74 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import createMultiSelectModel from '../create_multi_select_model';
|
||||
|
||||
describe('createMultiSelectModel', function () {
|
||||
|
||||
it('should throw an error if the first argument is not an array', () => {
|
||||
expect(createMultiSelectModel).withArgs('foo', []).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs(1234, []).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs(undefined, []).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs(null, []).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs([], []).to.not.throwError();
|
||||
});
|
||||
|
||||
it('should throw an error if the second argument is not an array', () => {
|
||||
expect(createMultiSelectModel).withArgs([], 'foo').to.throwError();
|
||||
expect(createMultiSelectModel).withArgs([], 1234).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs([], undefined).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs([], null).to.throwError();
|
||||
expect(createMultiSelectModel).withArgs([], []).to.not.throwError();
|
||||
});
|
||||
|
||||
it('should output an array with an item for each passed in', () => {
|
||||
const items = [ 'foo', 'bar', 'baz' ];
|
||||
const expected = [
|
||||
{ title: 'foo', selected: false },
|
||||
{ title: 'bar', selected: false },
|
||||
{ title: 'baz', selected: false }
|
||||
];
|
||||
const actual = createMultiSelectModel(items, []);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should set the selected property in the output', () => {
|
||||
const items = [ 'foo', 'bar', 'baz' ];
|
||||
const selectedItems = [ 'bar', 'baz' ];
|
||||
const expected = [
|
||||
{ title: 'foo', selected: false },
|
||||
{ title: 'bar', selected: true },
|
||||
{ title: 'baz', selected: true }
|
||||
];
|
||||
const actual = createMultiSelectModel(items, selectedItems);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should trim values when comparing for selected', () => {
|
||||
const items = [ 'foo', 'bar', 'baz' ];
|
||||
const selectedItems = [ ' bar ', ' baz ' ];
|
||||
const expected = [
|
||||
{ title: 'foo', selected: false },
|
||||
{ title: 'bar', selected: true },
|
||||
{ title: 'baz', selected: true }
|
||||
];
|
||||
const actual = createMultiSelectModel(items, selectedItems);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should be case insensitive when comparing for selected', () => {
|
||||
const items = [ 'foo', 'bar', 'baz' ];
|
||||
const selectedItems = [ ' Bar ', ' BAZ ' ];
|
||||
const expected = [
|
||||
{ title: 'foo', selected: false },
|
||||
{ title: 'bar', selected: true },
|
||||
{ title: 'baz', selected: true }
|
||||
];
|
||||
const actual = createMultiSelectModel(items, selectedItems);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,86 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import keysDeep from '../keys_deep';
|
||||
|
||||
describe('keys deep', function () {
|
||||
|
||||
it('should list first level properties', function () {
|
||||
let object = {
|
||||
property1: 'value1',
|
||||
property2: 'value2'
|
||||
};
|
||||
let expected = [
|
||||
'property1',
|
||||
'property2'
|
||||
];
|
||||
|
||||
const keys = keysDeep(object);
|
||||
|
||||
expect(keys).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should list nested properties', function () {
|
||||
let object = {
|
||||
property1: 'value1',
|
||||
property2: 'value2',
|
||||
property3: {
|
||||
subProperty1: 'value1.1'
|
||||
}
|
||||
};
|
||||
let expected = [
|
||||
'property1',
|
||||
'property2',
|
||||
'property3.subProperty1',
|
||||
'property3'
|
||||
];
|
||||
|
||||
const keys = keysDeep(object);
|
||||
|
||||
expect(keys).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should recursivly list nested properties', function () {
|
||||
let object = {
|
||||
property1: 'value1',
|
||||
property2: 'value2',
|
||||
property3: {
|
||||
subProperty1: 'value1.1',
|
||||
subProperty2: {
|
||||
prop1: 'value1.2.1',
|
||||
prop2: 'value2.2.2'
|
||||
},
|
||||
subProperty3: 'value1.3'
|
||||
}
|
||||
};
|
||||
let expected = [
|
||||
'property1',
|
||||
'property2',
|
||||
'property3.subProperty1',
|
||||
'property3.subProperty2.prop1',
|
||||
'property3.subProperty2.prop2',
|
||||
'property3.subProperty2',
|
||||
'property3.subProperty3',
|
||||
'property3'
|
||||
];
|
||||
|
||||
const keys = keysDeep(object);
|
||||
|
||||
expect(keys).to.eql(expected);
|
||||
});
|
||||
|
||||
it('should list array properties, but not contents', function () {
|
||||
let object = {
|
||||
property1: 'value1',
|
||||
property2: [ 'item1', 'item2' ]
|
||||
};
|
||||
let expected = [
|
||||
'property1',
|
||||
'property2'
|
||||
];
|
||||
|
||||
const keys = keysDeep(object);
|
||||
|
||||
expect(keys).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,480 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import Pipeline from '../pipeline';
|
||||
import * as processorTypes from '../../processors/view_models';
|
||||
|
||||
describe('processor pipeline', function () {
|
||||
|
||||
function getProcessorIds(pipeline) {
|
||||
return pipeline.processors.map(p => p.processorId);
|
||||
}
|
||||
|
||||
describe('model', function () {
|
||||
|
||||
it('should only contain the clean data properties', function () {
|
||||
const pipeline = new Pipeline();
|
||||
const actual = pipeline.model;
|
||||
const expectedKeys = [ 'input', 'processors' ];
|
||||
|
||||
expect(_.keys(actual)).to.eql(expectedKeys);
|
||||
});
|
||||
|
||||
it('should access the model property of each processor', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { foo: 'bar' };
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
const actual = pipeline.model;
|
||||
const expected = {
|
||||
input: pipeline.input,
|
||||
processors: [ pipeline.processors[0].model ]
|
||||
};
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('load', function () {
|
||||
|
||||
it('should remove existing processors from the pipeline', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const oldProcessors = [ pipeline.processors[0], pipeline.processors[1], pipeline.processors[2] ];
|
||||
|
||||
const newPipeline = new Pipeline();
|
||||
newPipeline.add(processorTypes.Set);
|
||||
newPipeline.add(processorTypes.Set);
|
||||
newPipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.load(newPipeline);
|
||||
|
||||
expect(_.find(pipeline.processors, oldProcessors[0])).to.be(undefined);
|
||||
expect(_.find(pipeline.processors, oldProcessors[1])).to.be(undefined);
|
||||
expect(_.find(pipeline.processors, oldProcessors[2])).to.be(undefined);
|
||||
});
|
||||
|
||||
it('should call addExisting for each of the imported processors', function () {
|
||||
const pipeline = new Pipeline();
|
||||
sinon.stub(pipeline, 'addExisting');
|
||||
|
||||
const newPipeline = new Pipeline();
|
||||
newPipeline.add(processorTypes.Set);
|
||||
newPipeline.add(processorTypes.Set);
|
||||
newPipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.load(newPipeline);
|
||||
|
||||
expect(pipeline.addExisting.calledWith(newPipeline.processors[0])).to.be(true);
|
||||
expect(pipeline.addExisting.calledWith(newPipeline.processors[1])).to.be(true);
|
||||
expect(pipeline.addExisting.calledWith(newPipeline.processors[2])).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('remove', function () {
|
||||
|
||||
it('remove the specified processor from the processors collection', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
pipeline.remove(pipeline.processors[1]);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('add', function () {
|
||||
|
||||
it('should append new items to the processors collection', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
expect(pipeline.processors.length).to.be(0);
|
||||
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
expect(pipeline.processors.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should append assign each new processor a unique processorId', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
const ids = pipeline.processors.map((p) => { return p.processorId; });
|
||||
expect(_.uniq(ids).length).to.be(3);
|
||||
});
|
||||
|
||||
it('added processors should be an instance of the type supplied', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
expect(pipeline.processors[0] instanceof processorTypes.Set).to.be(true);
|
||||
expect(pipeline.processors[1] instanceof processorTypes.Set).to.be(true);
|
||||
expect(pipeline.processors[2] instanceof processorTypes.Set).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('addExisting', function () {
|
||||
|
||||
it('should append new items to the processors collection', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
expect(pipeline.processors.length).to.be(0);
|
||||
|
||||
const testProcessor = new processorTypes.Set('foo');
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors.length).to.be(1);
|
||||
});
|
||||
|
||||
it('should instantiate an object of the same class as the object passed in', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
const testProcessor = new processorTypes.Set('foo');
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors[0] instanceof processorTypes.Set).to.be(true);
|
||||
});
|
||||
|
||||
it('the object added should be a different instance than the object passed in', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
const testProcessor = new processorTypes.Set('foo');
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors[0]).to.not.be(testProcessor);
|
||||
});
|
||||
|
||||
it('the object added should have the same property values as the object passed in (except id)', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
const testProcessor = new processorTypes.Set('foo');
|
||||
testProcessor.foo = 'bar';
|
||||
testProcessor.bar = 'baz';
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors[0].foo).to.be('bar');
|
||||
expect(pipeline.processors[0].bar).to.be('baz');
|
||||
expect(pipeline.processors[0].processorId).to.not.be('foo');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('moveUp', function () {
|
||||
|
||||
it('should be able to move an item up in the array', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[1];
|
||||
pipeline.moveUp(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[1]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[2]);
|
||||
});
|
||||
|
||||
it('should be able to move the same item move than once', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[2];
|
||||
pipeline.moveUp(target);
|
||||
pipeline.moveUp(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[2]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[1]);
|
||||
});
|
||||
|
||||
it('should not move the selected item past the top', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[2];
|
||||
pipeline.moveUp(target);
|
||||
pipeline.moveUp(target);
|
||||
pipeline.moveUp(target);
|
||||
pipeline.moveUp(target);
|
||||
pipeline.moveUp(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[2]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[1]);
|
||||
});
|
||||
|
||||
it('should not allow the top item to be moved up', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[0];
|
||||
pipeline.moveUp(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[1]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('moveDown', function () {
|
||||
|
||||
it('should be able to move an item down in the array', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[1];
|
||||
pipeline.moveDown(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[2]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[1]);
|
||||
});
|
||||
|
||||
it('should be able to move the same item move than once', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[0];
|
||||
pipeline.moveDown(target);
|
||||
pipeline.moveDown(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[1]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[2]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[0]);
|
||||
});
|
||||
|
||||
it('should not move the selected item past the bottom', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[0];
|
||||
pipeline.moveDown(target);
|
||||
pipeline.moveDown(target);
|
||||
pipeline.moveDown(target);
|
||||
pipeline.moveDown(target);
|
||||
pipeline.moveDown(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[1]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[2]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[0]);
|
||||
});
|
||||
|
||||
it('should not allow the bottom item to be moved down', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const target = pipeline.processors[2];
|
||||
pipeline.moveDown(target);
|
||||
|
||||
expect(pipeline.processors[0].processorId).to.be(processorIds[0]);
|
||||
expect(pipeline.processors[1].processorId).to.be(processorIds[1]);
|
||||
expect(pipeline.processors[2].processorId).to.be(processorIds[2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('updateParents', function () {
|
||||
|
||||
it('should set the first processors parent to pipeline.input', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { foo: 'bar' };
|
||||
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.processors.forEach(p => sinon.stub(p, 'setParent'));
|
||||
|
||||
pipeline.updateParents();
|
||||
|
||||
expect(pipeline.processors[0].setParent.calledWith(pipeline.input)).to.be(true);
|
||||
});
|
||||
|
||||
it('should set non-first processors parent to previous processor', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { foo: 'bar' };
|
||||
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.processors.forEach(p => sinon.stub(p, 'setParent'));
|
||||
|
||||
pipeline.updateParents();
|
||||
|
||||
expect(pipeline.processors[1].setParent.calledWith(pipeline.processors[0])).to.be(true);
|
||||
expect(pipeline.processors[2].setParent.calledWith(pipeline.processors[1])).to.be(true);
|
||||
expect(pipeline.processors[3].setParent.calledWith(pipeline.processors[2])).to.be(true);
|
||||
});
|
||||
|
||||
it('should set pipeline.dirty', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.updateParents();
|
||||
|
||||
expect(pipeline.dirty).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getProcessorById', function () {
|
||||
|
||||
it('should return a processor when suppied its id', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
const processorIds = getProcessorIds(pipeline);
|
||||
|
||||
const actual = pipeline.getProcessorById(processorIds[2]);
|
||||
const expected = pipeline.processors[2];
|
||||
|
||||
expect(actual).to.be(expected);
|
||||
});
|
||||
|
||||
it('should throw an error if given an unknown id', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
expect(pipeline.getProcessorById).withArgs('foo').to.throwError();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('updateOutput', function () {
|
||||
|
||||
it('should set the output to input if first processor has error', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { bar: 'baz' };
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.processors[0].outputObject = { field1: 'value1' };
|
||||
pipeline.processors[0].error = {}; //define an error
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.be(pipeline.input);
|
||||
});
|
||||
|
||||
it('should set the output to the processor before the error on a compile error', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.processors[0].outputObject = { field1: 'value1' };
|
||||
pipeline.processors[1].outputObject = { field1: 'value2' };
|
||||
pipeline.processors[2].outputObject = { field1: 'value3' };
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.eql({ field1: 'value3' });
|
||||
|
||||
pipeline.processors[1].error = { compile: true }; //define a compile error
|
||||
pipeline.processors[0].locked = true; //all other processors get locked.
|
||||
pipeline.processors[2].locked = true; //all other processors get locked.
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.eql({ field1: 'value1' });
|
||||
});
|
||||
|
||||
it('should set the output to the last processor with valid output if a processor has an error', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
pipeline.processors[0].outputObject = { field1: 'value1' };
|
||||
pipeline.processors[1].outputObject = { field1: 'value2' };
|
||||
pipeline.processors[2].outputObject = { field1: 'value3' };
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.eql({ field1: 'value3' });
|
||||
|
||||
pipeline.processors[2].error = {}; //define an error
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.eql({ field1: 'value2' });
|
||||
|
||||
pipeline.processors[1].error = {}; //define an error
|
||||
pipeline.processors[2].error = undefined; //if processor[1] has an error,
|
||||
pipeline.processors[2].locked = true; //subsequent processors will be locked.
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.eql({ field1: 'value1' });
|
||||
});
|
||||
|
||||
it('should set output to be last processor output if processors exist', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { bar: 'baz' };
|
||||
pipeline.add(processorTypes.Set);
|
||||
|
||||
const expected = { foo: 'bar' };
|
||||
pipeline.processors[0].outputObject = expected;
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.be(expected);
|
||||
});
|
||||
|
||||
it('should set output to be equal to input if no processors exist', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.input = { bar: 'baz' };
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.be(pipeline.input);
|
||||
});
|
||||
|
||||
it('should set pipeline.dirty', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.updateParents();
|
||||
expect(pipeline.dirty).to.be(true);
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.dirty).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// describe('applySimulateResults', function () { });
|
||||
|
||||
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
export default function selectableArray(items, selectedItems) {
|
||||
if (!_.isArray(items)) throw new Error('First argument must be an array');
|
||||
if (!_.isArray(selectedItems)) throw new Error('Second argument must be an array');
|
||||
|
||||
return items.map((item) => {
|
||||
const selected = _.find(selectedItems, (selectedItem) => {
|
||||
return cleanItem(selectedItem) === cleanItem(item);
|
||||
});
|
||||
|
||||
return {
|
||||
title: item,
|
||||
selected: !_.isUndefined(selected)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function cleanItem(item) {
|
||||
return _.trim(item).toUpperCase();
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
export default function keysDeep(object, base) {
|
||||
let result = [];
|
||||
let delimitedBase = base ? base + '.' : '';
|
||||
|
||||
_.forEach(object, (value, key) => {
|
||||
var fullKey = delimitedBase + key;
|
||||
if (_.isPlainObject(value)) {
|
||||
result = result.concat(keysDeep(value, fullKey));
|
||||
} else {
|
||||
result.push(fullKey);
|
||||
}
|
||||
});
|
||||
|
||||
if (base) {
|
||||
result.push(base);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
|
@ -1,176 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
function updateProcessorOutputs(pipeline, simulateResults) {
|
||||
simulateResults.forEach((result) => {
|
||||
const processor = pipeline.getProcessorById(result.processorId);
|
||||
|
||||
processor.outputObject = _.get(result, 'output');
|
||||
processor.error = _.get(result, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
//Updates the error state of the pipeline and its processors
|
||||
//If a pipeline compile error is returned, lock all processors but the error
|
||||
//If a pipeline data error is returned, lock all processors after the error
|
||||
function updateErrorState(pipeline) {
|
||||
pipeline.hasCompileError = _.some(pipeline.processors, (processor) => {
|
||||
return _.get(processor, 'error.compile');
|
||||
});
|
||||
_.forEach(pipeline.processors, processor => {
|
||||
processor.locked = false;
|
||||
});
|
||||
|
||||
const errorIndex = _.findIndex(pipeline.processors, 'error');
|
||||
if (errorIndex === -1) return;
|
||||
|
||||
_.forEach(pipeline.processors, (processor, index) => {
|
||||
if (pipeline.hasCompileError && index !== errorIndex) {
|
||||
processor.locked = true;
|
||||
}
|
||||
if (!pipeline.hasCompileError && index > errorIndex) {
|
||||
processor.locked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateProcessorInputs(pipeline) {
|
||||
pipeline.processors.forEach((processor) => {
|
||||
//we don't want to change the inputObject if the parent processor
|
||||
//is in error because that can cause us to lose state.
|
||||
if (!_.get(processor, 'parent.error')) {
|
||||
//the parent property of the first processor is set to the pipeline.input.
|
||||
//In all other cases it is set to processor[index-1]
|
||||
if (!processor.parent.processorId) {
|
||||
processor.inputObject = _.cloneDeep(processor.parent);
|
||||
} else {
|
||||
processor.inputObject = _.cloneDeep(processor.parent.outputObject);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export default class Pipeline {
|
||||
|
||||
constructor() {
|
||||
this.processors = [];
|
||||
this.processorCounter = 0;
|
||||
this.input = {};
|
||||
this.output = undefined;
|
||||
this.dirty = false;
|
||||
this.hasCompileError = false;
|
||||
}
|
||||
|
||||
get model() {
|
||||
const pipeline = {
|
||||
input: this.input,
|
||||
processors: _.map(this.processors, processor => processor.model)
|
||||
};
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
setDirty() {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
load(pipeline) {
|
||||
this.processors = [];
|
||||
pipeline.processors.forEach((processor) => {
|
||||
this.addExisting(processor);
|
||||
});
|
||||
}
|
||||
|
||||
remove(processor) {
|
||||
const processors = this.processors;
|
||||
const index = processors.indexOf(processor);
|
||||
|
||||
processors.splice(index, 1);
|
||||
}
|
||||
|
||||
moveUp(processor) {
|
||||
const processors = this.processors;
|
||||
const index = processors.indexOf(processor);
|
||||
|
||||
if (index === 0) return;
|
||||
|
||||
const temp = processors[index - 1];
|
||||
processors[index - 1] = processors[index];
|
||||
processors[index] = temp;
|
||||
}
|
||||
|
||||
moveDown(processor) {
|
||||
const processors = this.processors;
|
||||
const index = processors.indexOf(processor);
|
||||
|
||||
if (index === processors.length - 1) return;
|
||||
|
||||
const temp = processors[index + 1];
|
||||
processors[index + 1] = processors[index];
|
||||
processors[index] = temp;
|
||||
}
|
||||
|
||||
addExisting(existingProcessor) {
|
||||
const Type = existingProcessor.constructor;
|
||||
const newProcessor = this.add(Type);
|
||||
_.assign(newProcessor, _.omit(existingProcessor, 'processorId'));
|
||||
|
||||
return newProcessor;
|
||||
}
|
||||
|
||||
add(ProcessorType) {
|
||||
const processors = this.processors;
|
||||
|
||||
this.processorCounter += 1;
|
||||
const processorId = `processor_${this.processorCounter}`;
|
||||
const newProcessor = new ProcessorType(processorId);
|
||||
processors.push(newProcessor);
|
||||
|
||||
return newProcessor;
|
||||
}
|
||||
|
||||
updateParents() {
|
||||
const processors = this.processors;
|
||||
|
||||
processors.forEach((processor, index) => {
|
||||
let newParent;
|
||||
if (index === 0) {
|
||||
newParent = this.input;
|
||||
} else {
|
||||
newParent = processors[index - 1];
|
||||
}
|
||||
|
||||
processor.setParent(newParent);
|
||||
});
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
getProcessorById(processorId) {
|
||||
const result = _.find(this.processors, { processorId });
|
||||
|
||||
if (!result) {
|
||||
throw new Error(`Could not find processor by id [${processorId}]`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
updateOutput() {
|
||||
const processors = this.processors;
|
||||
|
||||
const errorIndex = _.findIndex(processors, 'error');
|
||||
const goodProcessor = errorIndex === -1 ? _.last(processors) : processors[errorIndex - 1];
|
||||
this.output = goodProcessor ? goodProcessor.outputObject : this.input;
|
||||
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
// Updates the state of the pipeline and processors with the results
|
||||
// from an ingest simulate call.
|
||||
applySimulateResults(simulateResults) {
|
||||
updateProcessorOutputs(this, simulateResults);
|
||||
updateErrorState(this);
|
||||
updateProcessorInputs(this);
|
||||
this.updateOutput();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiAppend', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
function splitValues(delimitedList) {
|
||||
return delimitedList.split('\n');
|
||||
}
|
||||
|
||||
function joinValues(valueArray) {
|
||||
return valueArray.join('\n');
|
||||
}
|
||||
|
||||
function updateValues() {
|
||||
processor.values = splitValues($scope.values);
|
||||
}
|
||||
|
||||
$scope.values = joinValues(processor.values);
|
||||
|
||||
$scope.$watch('values', updateValues);
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
$scope.$watchCollection('processor.values', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Values:</label><span> (line delimited)</span>
|
||||
<textarea ng-model="values" class="form-control"></textarea>
|
||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Append extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'append', 'Append');
|
||||
this.targetField = '';
|
||||
this.values = [];
|
||||
}
|
||||
|
||||
get description() {
|
||||
const target = this.targetField || '?';
|
||||
return `[${target}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
targetField: this.targetField || '',
|
||||
values: this.values || []
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
export default class Processor {
|
||||
constructor(processorId, typeId, title) {
|
||||
if (!typeId || !title) {
|
||||
throw new Error('Cannot instantiate the base Processor class.');
|
||||
}
|
||||
|
||||
this.processorId = processorId;
|
||||
this.title = title;
|
||||
this.typeId = typeId;
|
||||
this.collapsed = false;
|
||||
this.parent = undefined;
|
||||
this.inputObject = undefined;
|
||||
this.outputObject = undefined;
|
||||
this.error = undefined;
|
||||
}
|
||||
|
||||
setParent(newParent) {
|
||||
const oldParent = this.parent;
|
||||
this.parent = newParent;
|
||||
|
||||
return (oldParent !== this.parent);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiConvert', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.types = ['auto', 'number', 'string', 'boolean'];
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.type', processorUiChanged);
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Type:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="type as type for type in types"
|
||||
ng-model="processor.type">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
|
@ -1,28 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import Processor from '../base/view_model';
|
||||
|
||||
export class Convert extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'convert', 'Convert');
|
||||
this.sourceField = '';
|
||||
this.targetField = '';
|
||||
this.type = 'auto';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const type = this.type || '?';
|
||||
const target = this.targetField ? ` -> [${this.targetField}]` : '';
|
||||
return `[${source}] to ${type}${target}`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
targetField: this.targetField || '',
|
||||
type: this.type || 'auto'
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,58 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import createMultiSelectModel from '../../lib/create_multi_select_model';
|
||||
import template from './view.html';
|
||||
import './styles.less';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiDate', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope, debounce) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
const updateFormats = debounce(() => {
|
||||
processor.formats = _($scope.formats)
|
||||
.filter('selected')
|
||||
.map('title')
|
||||
.value();
|
||||
|
||||
$scope.customFormatSelected = _.includes(processor.formats, 'Custom');
|
||||
processorUiChanged();
|
||||
}, 200);
|
||||
|
||||
$scope.updateFormats = updateFormats;
|
||||
$scope.formats = createMultiSelectModel(['ISO8601', 'UNIX', 'UNIX_MS', 'TAI64N', 'Custom'], processor.formats);
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.customFormat', updateFormats);
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
$scope.$watch('processor.timezone', processorUiChanged);
|
||||
$scope.$watch('processor.locale', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
processor-ui-date {
|
||||
.custom-date-format {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Formats:</label>
|
||||
<div ng-repeat="format in formats">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="format_{{processor.processorId}}_{{$index}}"
|
||||
ng-model="format.selected"
|
||||
ng-click="updateFormats()" />
|
||||
<label for="format_{{processor.processorId}}_{{$index}}">
|
||||
{{format.title}}
|
||||
<a
|
||||
aria-label="Custom Date Format Help"
|
||||
tooltip="Custom Date Format Help"
|
||||
tooltip-append-to-body="true"
|
||||
href="http://www.joda.org/joda-time/key_format.html"
|
||||
target="_blank"
|
||||
ng-show="format.title === 'Custom'">
|
||||
<i aria-hidden="true" class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="custom-date-format"
|
||||
ng-show="customFormatSelected">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="processor.customFormat">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Timezone:
|
||||
<a
|
||||
aria-label="Timezone Help"
|
||||
tooltip="Timezone Help"
|
||||
tooltip-append-to-body="true"
|
||||
href="http://joda-time.sourceforge.net/timezones.html"
|
||||
target="_blank">
|
||||
<i aria-hidden="true" class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<input type="text" class="form-control" ng-model="processor.timezone"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Locale:
|
||||
<a
|
||||
aria-label="Locale Help"
|
||||
tooltip="Locale Help"
|
||||
tooltip-append-to-body="true"
|
||||
href="https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html"
|
||||
target="_blank">
|
||||
<i aria-hidden="true" class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<input type="text" class="form-control" ng-model="processor.locale"></div>
|
||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Date extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'date', 'Date');
|
||||
this.sourceField = '';
|
||||
this.targetField = '@timestamp';
|
||||
this.formats = [];
|
||||
this.timezone = 'Etc/UTC';
|
||||
this.locale = 'ENGLISH';
|
||||
this.customFormat = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const target = this.targetField || '?';
|
||||
return `[${source}] -> [${target}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
targetField: this.targetField || '',
|
||||
formats: this.formats || [],
|
||||
timezone: this.timezone || '',
|
||||
locale: this.locale || '',
|
||||
customFormat: this.customFormat || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
import './styles.less';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiGeoip', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
function splitValues(delimitedList) {
|
||||
return delimitedList.split('\n');
|
||||
}
|
||||
|
||||
function joinValues(valueArray) {
|
||||
return valueArray.join('\n');
|
||||
}
|
||||
|
||||
function updateDatabaseFields() {
|
||||
const fieldsString = $scope.databaseFields.replace(/,/g, '\n');
|
||||
processor.databaseFields = _(splitValues(fieldsString)).map(_.trim).compact().value();
|
||||
$scope.databaseFields = joinValues(processor.databaseFields);
|
||||
}
|
||||
|
||||
$scope.databaseFields = joinValues(processor.databaseFields);
|
||||
|
||||
$scope.$watch('databaseFields', updateDatabaseFields);
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
$scope.$watch('processor.databaseFile', processorUiChanged);
|
||||
$scope.$watchCollection('processor.databaseFields', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
processor-ui-geoip {
|
||||
.advanced-section {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.advanced-section-heading {
|
||||
.btn {
|
||||
background-color: transparent;
|
||||
color: black;
|
||||
border: transparent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
||||
|
||||
<div class="advanced-section">
|
||||
<div class="form-group advanced-section-heading">
|
||||
<button
|
||||
ng-click="processor.advancedExpanded = !processor.advancedExpanded"
|
||||
type="button"
|
||||
class="btn btn-default btn-xs processor-ui-container-header-toggle">
|
||||
<i
|
||||
aria-hidden="true"
|
||||
ng-class="{ 'fa-caret-down': processor.advancedExpanded, 'fa-caret-right': !processor.advancedExpanded }"
|
||||
class="fa">
|
||||
</i>
|
||||
</button>
|
||||
<label ng-click="processor.advancedExpanded = !processor.advancedExpanded">Advanced Settings</label>
|
||||
</div>
|
||||
<div ng-show="processor.advancedExpanded">
|
||||
<div class="form-group">
|
||||
<label>Database File:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.databaseFile">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Data Fields:</label><span> (line delimited)</span>
|
||||
<textarea ng-model="databaseFields" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,28 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class GeoIp extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'geoip', 'Geo IP');
|
||||
this.sourceField = '';
|
||||
this.targetField = '';
|
||||
this.databaseFile = '';
|
||||
this.databaseFields = [];
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const target = this.targetField || '?';
|
||||
return `[${source}] -> [${target}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
targetField: this.targetField || '',
|
||||
databaseFile: this.databaseFile || '',
|
||||
databaseFields: this.databaseFields || []
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiGrok', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.pattern', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Pattern:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.pattern">
|
||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import Processor from '../base/view_model';
|
||||
|
||||
export class Grok extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'grok', 'Grok');
|
||||
this.sourceField = '';
|
||||
this.pattern = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const inputKeys = keysDeep(this.inputObject);
|
||||
const outputKeys = keysDeep(this.outputObject);
|
||||
const addedKeys = _.difference(outputKeys, inputKeys);
|
||||
const added = addedKeys.sort().map(field => `[${field}]`).join(', ');
|
||||
const source = this.sourceField || '?';
|
||||
|
||||
return `[${source}] -> ${added}`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
pattern: this.pattern || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiGsub', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.pattern', processorUiChanged);
|
||||
$scope.$watch('processor.replacement', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Pattern:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.pattern">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Replacement:</label>
|
||||
<input type="text" class="form-control" ng-trim="false" ng-model="processor.replacement">
|
||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Gsub extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'gsub', 'Gsub');
|
||||
this.sourceField = '';
|
||||
this.pattern = '';
|
||||
this.replacement = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
return `[${source}] - /${this.pattern}/ -> '${this.replacement}'`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
pattern: this.pattern || '',
|
||||
replacement: this.replacement || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
import './append/directive';
|
||||
import './convert/directive';
|
||||
import './date/directive';
|
||||
import './geoip/directive';
|
||||
import './grok/directive';
|
||||
import './gsub/directive';
|
||||
import './join/directive';
|
||||
import './lowercase/directive';
|
||||
import './remove/directive';
|
||||
import './rename/directive';
|
||||
import './set/directive';
|
||||
import './split/directive';
|
||||
import './trim/directive';
|
||||
import './uppercase/directive';
|
|
@ -1,41 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiJoin', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
const allKeys = keysDeep(processor.inputObject);
|
||||
$scope.fields = _.filter(allKeys, (key) => { return _.isArray(_.get(processor.inputObject, key)); });
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.separator', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Array Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Separator:</label>
|
||||
<input type="text" class="form-control" ng-trim="false" ng-model="processor.separator">
|
||||
</div>
|
|
@ -1,24 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Join extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'join', 'Join');
|
||||
this.sourceField = '';
|
||||
this.separator = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const separator = this.separator ? ` on '${this.separator}'` : '';
|
||||
return `[${source}]${separator}`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
separator: this.separator || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiLowercase', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
const allKeys = keysDeep(processor.inputObject);
|
||||
$scope.fields = _.filter(allKeys, (key) => { return _.isString(_.get(processor.inputObject, key)); });
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Lowercase extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'lowercase', 'Lowercase');
|
||||
this.sourceField = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
return `[${source}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,38 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiRemove', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Remove extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'remove', 'Remove');
|
||||
this.sourceField = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
return `[${source}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiRename', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
$scope.fields = keysDeep(processor.inputObject);
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
|
@ -1,24 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Rename extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'rename', 'Rename');
|
||||
this.sourceField = '';
|
||||
this.targetField = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const target = this.targetField || '?';
|
||||
return `[${source}] -> [${target}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
targetField: this.targetField || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiSet', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
$scope.$watch('processor.value', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Target Field:</label>
|
||||
<input type="text" class="form-control" ng-model="processor.targetField">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Value:</label>
|
||||
<input type="text" class="form-control" ng-trim="false" ng-model="processor.value">
|
||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Set extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'set', 'Set');
|
||||
this.targetField = '';
|
||||
this.value = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const target = this.targetField || '?';
|
||||
return `[${target}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
targetField: this.targetField || '',
|
||||
value: this.value || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiSplit', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
const allKeys = keysDeep(processor.inputObject);
|
||||
$scope.fields = _.filter(allKeys, (key) => { return _.isString(_.get(processor.inputObject, key)); });
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
|
||||
$scope.$watch('processor.separator', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Separator:</label>
|
||||
<input type="text" class="form-control" ng-trim="false" ng-model="processor.separator">
|
||||
</div>
|
|
@ -1,24 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Split extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'split', 'Split');
|
||||
this.sourceField = '';
|
||||
this.separator = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
const separator = this.separator || '?';
|
||||
return `[${source}] on '${separator}'`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || '',
|
||||
separator: this.separator || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiTrim', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
const allKeys = keysDeep(processor.inputObject);
|
||||
$scope.fields = _.filter(allKeys, (key) => { return _.isString(_.get(processor.inputObject, key)); });
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Trim extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'trim', 'Trim');
|
||||
this.sourceField = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
return `[${source}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import uiModules from 'ui/modules';
|
||||
import keysDeep from '../../lib/keys_deep';
|
||||
import template from './view.html';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiUppercase', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function consumeNewInputObject() {
|
||||
const allKeys = keysDeep(processor.inputObject);
|
||||
$scope.fields = _.filter(allKeys, (key) => { return _.isString(_.get(processor.inputObject, key)); });
|
||||
refreshFieldData();
|
||||
}
|
||||
|
||||
function refreshFieldData() {
|
||||
$scope.fieldData = _.get(processor.inputObject, processor.sourceField);
|
||||
}
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.setDirty();
|
||||
}
|
||||
|
||||
$scope.$watch('processor.inputObject', consumeNewInputObject);
|
||||
|
||||
$scope.$watch('processor.sourceField', () => {
|
||||
refreshFieldData();
|
||||
processorUiChanged();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
<div class="form-group">
|
||||
<label>Field:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="field as field for field in fields"
|
||||
ng-model="processor.sourceField">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Field Data:</label>
|
||||
<pre>{{ fieldData }}</pre>
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
import Processor from '../base/view_model';
|
||||
|
||||
export class Uppercase extends Processor {
|
||||
constructor(processorId) {
|
||||
super(processorId, 'uppercase', 'Uppercase');
|
||||
this.sourceField = '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const source = this.sourceField || '?';
|
||||
return `[${source}]`;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
processorId: this.processorId,
|
||||
typeId: this.typeId,
|
||||
sourceField: this.sourceField || ''
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
export { Append } from './append/view_model';
|
||||
export { Convert } from './convert/view_model';
|
||||
export { Date } from './date/view_model';
|
||||
export { GeoIp } from './geoip/view_model';
|
||||
export { Grok } from './grok/view_model';
|
||||
export { Gsub } from './gsub/view_model';
|
||||
export { Join } from './join/view_model';
|
||||
export { Lowercase } from './lowercase/view_model';
|
||||
export { Remove } from './remove/view_model';
|
||||
export { Rename } from './rename/view_model';
|
||||
export { Set } from './set/view_model';
|
||||
export { Split } from './split/view_model';
|
||||
export { Trim } from './trim/view_model';
|
||||
export { Uppercase } from './uppercase/view_model';
|
|
@ -1,28 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
output-preview {
|
||||
.visual {
|
||||
border: none;
|
||||
background-color: @settings-filebeat-wizard-panel-bg;
|
||||
border-radius: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.visual.collapsed {
|
||||
max-height: 125px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.hide-unchanged {
|
||||
.jsondiffpatch-unchanged {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
pipeline-output {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.header-line {
|
||||
display: flex;
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
min-height: 450px;
|
||||
flex: 1 1 1px;
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
pipeline-setup {
|
||||
.main-panels {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.left-panel {
|
||||
.flex-parent(1, 1, 1px);
|
||||
width: 50%;
|
||||
|
||||
&>label {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.center-panel {
|
||||
.flex-parent(0, 0, auto, column);
|
||||
justify-content: center;
|
||||
|
||||
.buttons {
|
||||
.flex-parent(0, 0, auto, column);
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
.flex-parent(1, 1, 1px, column);
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.pipeline {
|
||||
min-height: 450px;
|
||||
background-color: @settings-filebeat-wizard-panel-bg;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
ul.pipeline-container {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&>li {
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-processor {
|
||||
padding:10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.add-processor-dropdown {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
select.form-control {
|
||||
background-color: @settings-filebeat-wizard-processor-select-bg;
|
||||
border: none;
|
||||
width: auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
textarea.form-control {
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
processor-ui-container {
|
||||
display: block;
|
||||
margin-bottom: 1px;
|
||||
border-bottom: 2px solid;
|
||||
border-color: white;
|
||||
|
||||
.processor-ui-container-body {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.overlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
top: -5000px;
|
||||
left: -5000px;
|
||||
width: 10000px;
|
||||
height: 10000px;
|
||||
background-color: @settings-filebeat-wizard-processor-container-overlay-bg;
|
||||
}
|
||||
|
||||
&.locked {
|
||||
.overlay {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.processor-ui-container-body-content {
|
||||
padding: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
processor-ui-container-header {
|
||||
.processor-ui-container-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1 0 auto;
|
||||
background-color: @settings-filebeat-wizard-panel-bg;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
|
||||
button {
|
||||
width: 22px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.processor-ui-container-header-toggle {
|
||||
flex: 0 0 auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.processor-ui-container-header-title {
|
||||
flex: 1 1 auto;
|
||||
.ellipsis();
|
||||
font-weight: bold;
|
||||
|
||||
.processor-title {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.processor-description {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.processor-description.danger {
|
||||
font-weight: bold;
|
||||
color: @brand-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.processor-ui-container-header-controls {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
source-data {
|
||||
flex: 1 0 auto;
|
||||
display: flex;
|
||||
height: 22px;
|
||||
|
||||
button {
|
||||
flex: 0 0 auto;
|
||||
width: 22px;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<div class="form-group">
|
||||
|
||||
<label>Processor Changes:</label>
|
||||
<a
|
||||
style="float: right"
|
||||
ng-click="collapsed = true"
|
||||
ng-hide="collapsed">collapse</a>
|
||||
<a
|
||||
style="float: right"
|
||||
ng-click="collapsed = false"
|
||||
ng-show="collapsed">expand</a>
|
||||
<span style="float: right"> / </span>
|
||||
<a
|
||||
style="float: right"
|
||||
ng-click="showAll = false"
|
||||
ng-show="showAll">only show changes</a>
|
||||
<a
|
||||
style="float: right"
|
||||
ng-click="showAll = true"
|
||||
ng-hide="showAll">show all</a>
|
||||
<div
|
||||
class="visual"
|
||||
ng-class="{'hide-unchanged': !showAll, collapsed: collapsed}"></div>
|
||||
</div>
|
|
@ -1,18 +0,0 @@
|
|||
<div class="header-line">
|
||||
<label>
|
||||
Pipeline Output
|
||||
<a
|
||||
aria-label="The pipeline output shows the result of the defined pipeline using the sample records supplied in the previous step."
|
||||
tooltip="The pipeline output shows the result of the defined pipeline using the sample records supplied in the previous step."
|
||||
tooltip-append-to-body="true"
|
||||
target="_blank">
|
||||
<i aria-hidden="true" class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<source-data
|
||||
sample="sample"
|
||||
samples="samples"
|
||||
disabled="pipeline.hasCompileError">
|
||||
</source-data>
|
||||
</div>
|
||||
<pre class="output">{{ pipeline.output | json }}</pre>
|
|
@ -1,74 +0,0 @@
|
|||
<h2>
|
||||
<em>Let's build a pipeline!</em> Ingest pipelines are an easy way to modify documents before they're indexed in Elasticsearch. They're composed of processors which can change your data in many ways. Create a pipeline below while cycling through your samples to see its effect on your data.
|
||||
</h2>
|
||||
<div class="main-panels">
|
||||
<div
|
||||
ng-hide="expandContext < 1"
|
||||
class="left-panel">
|
||||
<label>
|
||||
Processor Pipeline
|
||||
<a
|
||||
aria-label="A pipeline is a definition of a series of processors that are to be executed in the same order as they are declared."
|
||||
tooltip="A pipeline is a definition of a series of processors that are to be executed in the same order as they are declared."
|
||||
tooltip-append-to-body="true"
|
||||
target="_blank">
|
||||
<i aria-hidden="true" class="fa fa-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div class="pipeline">
|
||||
<ul
|
||||
class="pipeline-container"
|
||||
ng-show="pipeline.processors.length > 0">
|
||||
<li ng-repeat="processor in pipeline.processors track by processor.processorId">
|
||||
<processor-ui-container pipeline="pipeline" processor="processor"></processor-ui-container>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="add-processor">
|
||||
<div
|
||||
class="form-group"
|
||||
ng-hide="pipeline.processors.length > 0">
|
||||
<label>
|
||||
Your pipeline is currently empty. Add a processor to get started!
|
||||
</label>
|
||||
</div>
|
||||
<div class="add-processor-dropdown">
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="processorType.title for processorType in processorTypes"
|
||||
ng-model="processorType"
|
||||
ng-disabled="pipeline.hasCompileError">
|
||||
<option value="">Select a Processor...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center-panel">
|
||||
<div class="buttons">
|
||||
<button
|
||||
aria-label="{{expandContext > 1 ? 'Expand Right Panel' : 'Collapse Left Panel'}}"
|
||||
tooltip="{{expandContext > 1 ? 'Expand Right Panel' : 'Collapse Left Panel'}}"
|
||||
ng-click="expandContext = expandContext - 1"
|
||||
ng-disabled="expandContext < 1"
|
||||
type="button"
|
||||
class="btn btn-primary btn-xs collapser">
|
||||
<i aria-hidden="true" class="fa fa-chevron-circle-left"></i>
|
||||
</button>
|
||||
<button
|
||||
aria-label="{{expandContext < 1 ? 'Expand Left Panel' : 'Collapse Right Panel'}}"
|
||||
tooltip="{{expandContext < 1 ? 'Expand Left Panel' : 'Collapse Right Panel'}}"
|
||||
ng-click="expandContext = expandContext + 1"
|
||||
ng-disabled="expandContext > 1"
|
||||
type="button"
|
||||
class="btn btn-primary btn-xs collapser">
|
||||
<i aria-hidden="true" class="fa fa-chevron-circle-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ng-hide="expandContext > 1"
|
||||
class="right-panel">
|
||||
<pipeline-output pipeline="pipeline" samples="samples" sample="sample"></pipeline-output>
|
||||
</div>
|
||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||
<processor-ui-container-header
|
||||
processor="processor"
|
||||
field="sourceField"
|
||||
pipeline="pipeline">
|
||||
</processor-ui-container-header>
|
||||
<div
|
||||
class="processor-ui-container-body"
|
||||
ng-class="{locked: processor.locked}">
|
||||
<div
|
||||
class="processor-ui-container-body-content"
|
||||
ng-hide="processor.collapsed">
|
||||
<div
|
||||
ng-show="processor.error"
|
||||
class="alert alert-danger">
|
||||
{{processor.error.message}}
|
||||
</div>
|
||||
<div class="processor-ui-content"></div>
|
||||
<output-preview
|
||||
new-object="processor.outputObject"
|
||||
old-object="processor.inputObject"
|
||||
error="processor.error">
|
||||
</output-preview>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
|
@ -1,61 +0,0 @@
|
|||
<div class="processor-ui-container-header">
|
||||
<button
|
||||
aria-label="{{ processor.collapsed ? 'Expand Processor' : 'Collapse Processor' }}"
|
||||
tooltip="{{ processor.collapsed ? 'Expand Processor' : 'Collapse Processor' }}"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="processor.collapsed = !processor.collapsed"
|
||||
type="button"
|
||||
class="btn btn-primary btn-xs processor-ui-container-header-toggle">
|
||||
<i aria-hidden="true" ng-class="{ 'fa-caret-down': !processor.collapsed, 'fa-caret-right': processor.collapsed }" class="fa"></i>
|
||||
</button>
|
||||
|
||||
<div class="processor-ui-container-header-title">
|
||||
<span class="processor-title">
|
||||
{{processor.title}}
|
||||
</span>
|
||||
|
||||
<span class="processor-description">
|
||||
- {{ processor.description }}
|
||||
</span>
|
||||
|
||||
<!-- error -->
|
||||
<span ng-if="processor.error" class="processor-description danger">
|
||||
- Error
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="processor-ui-container-header-controls btn-group">
|
||||
<button
|
||||
aria-label="Increase Priority"
|
||||
tooltip="Increase Priority"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="pipeline.moveUp(processor)"
|
||||
type="button"
|
||||
class="btn btn-xs btn-primary"
|
||||
ng-disabled="pipeline.hasCompileError">
|
||||
<i aria-hidden="true" class="fa fa-caret-up"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
aria-label="Decrease Priority"
|
||||
tooltip="Decrease Priority"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="pipeline.moveDown(processor)"
|
||||
type="button"
|
||||
class="btn btn-xs btn-primary"
|
||||
ng-disabled="pipeline.hasCompileError">
|
||||
<i aria-hidden="true" class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
|
||||
<button
|
||||
aria-label="Remove Processor"
|
||||
tooltip="Remove Processor"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="pipeline.remove(processor)"
|
||||
type="button"
|
||||
class="btn btn-xs btn-danger"
|
||||
ng-disabled="pipeline.hasCompileError && !processor.error">
|
||||
<i aria-hidden="true" class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
<button
|
||||
aria-label="Previous Sample"
|
||||
tooltip="Previous Sample"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="previousLine()"
|
||||
type="button"
|
||||
class="btn btn-xs btn-primary"
|
||||
ng-disabled="disabled">
|
||||
<i aria-hidden="true" class="fa fa-caret-left"></i>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Next Sample"
|
||||
tooltip="Next Sample"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="nextLine()"
|
||||
type="button"
|
||||
class="btn btn-xs btn-primary"
|
||||
ng-disabled="disabled">
|
||||
<i aria-hidden="true" class="fa fa-caret-right"></i>
|
||||
</button>
|
|
@ -19,6 +19,10 @@ kbn-management-objects-view {
|
|||
min-height: 70px; /* 1 */
|
||||
}
|
||||
|
||||
.tab-account {
|
||||
background-color: @kibanaGray6;
|
||||
}
|
||||
|
||||
.tab-management {
|
||||
background-color: @kibanaGray6;
|
||||
}
|
||||
|
|
|
@ -53,11 +53,11 @@ uiModules
|
|||
SavedVis.type = 'visualization';
|
||||
|
||||
SavedVis.mapping = {
|
||||
title: 'string',
|
||||
title: 'text',
|
||||
visState: 'json',
|
||||
uiStateJSON: 'string',
|
||||
description: 'string',
|
||||
savedSearchId: 'string',
|
||||
uiStateJSON: 'text',
|
||||
description: 'text',
|
||||
savedSearchId: 'keyword',
|
||||
version: 'integer'
|
||||
};
|
||||
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
import expect from 'expect.js';
|
||||
import _ from 'lodash';
|
||||
import ingestSimulateApiKibanaToEsConverter from '../../converters/ingest_simulate_api_kibana_to_es_converter';
|
||||
|
||||
describe('ingestSimulateApiKibanaToEsConverter', function () {
|
||||
|
||||
it('populates the docs._source section and converts known processors', function () {
|
||||
|
||||
function buildSamplePipeline(input) {
|
||||
return {
|
||||
processors: [ { processor_id: 'processor1', type_id: 'set', target_field: 'bar', value: 'foo' } ],
|
||||
input: input
|
||||
};
|
||||
}
|
||||
|
||||
function buildExpected(input) {
|
||||
return {
|
||||
pipeline : {
|
||||
processors: [{
|
||||
set: {
|
||||
field: 'bar',
|
||||
tag: 'processor1',
|
||||
value: 'foo'
|
||||
}
|
||||
}]
|
||||
},
|
||||
'docs' : [
|
||||
{ '_source': input }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
let expected;
|
||||
let actual;
|
||||
|
||||
expected = buildExpected(undefined);
|
||||
actual = ingestSimulateApiKibanaToEsConverter(buildSamplePipeline(undefined));
|
||||
expect(actual).to.eql(expected);
|
||||
|
||||
expected = buildExpected('foo');
|
||||
actual = ingestSimulateApiKibanaToEsConverter(buildSamplePipeline('foo'));
|
||||
expect(actual).to.eql(expected);
|
||||
|
||||
expected = buildExpected({ foo: 'bar' });
|
||||
actual = ingestSimulateApiKibanaToEsConverter(buildSamplePipeline({ foo: 'bar' }));
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('handles multiple processors', function () {
|
||||
const pipeline = {
|
||||
processors: [
|
||||
{ processor_id: 'processor1', type_id: 'set', target_field: 'bar', value: 'foo' },
|
||||
{ processor_id: 'processor2', type_id: 'set', target_field: 'bar', value: 'foo' },
|
||||
],
|
||||
input: {}
|
||||
};
|
||||
const expected = {
|
||||
'pipeline': {
|
||||
'processors': [
|
||||
{
|
||||
set: {
|
||||
field: 'bar',
|
||||
tag: 'processor1',
|
||||
value: 'foo'
|
||||
}
|
||||
},
|
||||
{
|
||||
set: {
|
||||
field: 'bar',
|
||||
tag: 'processor2',
|
||||
value: 'foo'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
'docs': [
|
||||
{'_source': {}}
|
||||
]
|
||||
};
|
||||
|
||||
const actual = ingestSimulateApiKibanaToEsConverter(pipeline);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
|
||||
});
|
|
@ -1,100 +0,0 @@
|
|||
import initDefaultFieldProps from '../init_default_field_props';
|
||||
import expect from 'expect.js';
|
||||
import _ from 'lodash';
|
||||
let fields;
|
||||
|
||||
const testData = [
|
||||
{
|
||||
'name': 'ip',
|
||||
'type': 'ip'
|
||||
}, {
|
||||
'name': '@timestamp',
|
||||
'type': 'date'
|
||||
}, {
|
||||
'name': 'agent',
|
||||
'type': 'string'
|
||||
}, {
|
||||
'name': 'bytes',
|
||||
'type': 'number'
|
||||
},
|
||||
{
|
||||
'name': 'geo.coordinates',
|
||||
'type': 'geo_point'
|
||||
}
|
||||
];
|
||||
|
||||
describe('initDefaultFieldProps', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
fields = _.cloneDeep(testData);
|
||||
});
|
||||
|
||||
it('should throw an error if no argument is passed or the argument is not an array', function () {
|
||||
expect(initDefaultFieldProps).to.throwException(/requires an array argument/);
|
||||
expect(initDefaultFieldProps).withArgs({}).to.throwException(/requires an array argument/);
|
||||
});
|
||||
|
||||
it('should set the same defaults for everything but strings', function () {
|
||||
const results = initDefaultFieldProps(fields);
|
||||
_.forEach(results, function (field) {
|
||||
if (field.type !== 'string') {
|
||||
expect(field).to.have.property('indexed', true);
|
||||
expect(field).to.have.property('analyzed', false);
|
||||
expect(field).to.have.property('doc_values', true);
|
||||
expect(field).to.have.property('scripted', false);
|
||||
expect(field).to.have.property('count', 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should make string fields analyzed', function () {
|
||||
const results = initDefaultFieldProps(fields);
|
||||
_.forEach(results, function (field) {
|
||||
if (field.type === 'string' && !_.contains(field.name, 'keyword')) {
|
||||
expect(field).to.have.property('indexed', true);
|
||||
expect(field).to.have.property('analyzed', true);
|
||||
expect(field).to.have.property('doc_values', false);
|
||||
expect(field).to.have.property('scripted', false);
|
||||
expect(field).to.have.property('count', 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an extra raw non-analyzed field for strings', function () {
|
||||
const results = initDefaultFieldProps(fields);
|
||||
const rawField = _.find(results, function (field) {
|
||||
return _.contains(field.name, 'keyword');
|
||||
});
|
||||
expect(rawField).to.have.property('indexed', true);
|
||||
expect(rawField).to.have.property('analyzed', false);
|
||||
expect(rawField).to.have.property('doc_values', true);
|
||||
expect(rawField).to.have.property('scripted', false);
|
||||
expect(rawField).to.have.property('count', 0);
|
||||
});
|
||||
|
||||
it('should apply some overrides to metafields', function () {
|
||||
const results = initDefaultFieldProps([{name: '_source'}, {name: '_timestamp'}]);
|
||||
const expected = [
|
||||
{
|
||||
name: '_source',
|
||||
indexed: false,
|
||||
analyzed: false,
|
||||
doc_values: false,
|
||||
count: 0,
|
||||
scripted: false,
|
||||
type: '_source'
|
||||
},
|
||||
{
|
||||
name: '_timestamp',
|
||||
indexed: true,
|
||||
analyzed: false,
|
||||
doc_values: false,
|
||||
count: 0,
|
||||
scripted: false,
|
||||
type: 'date'
|
||||
}
|
||||
];
|
||||
|
||||
expect(_.isEqual(expected, results)).to.be.ok();
|
||||
});
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
import processESIngestProcessorsResponse from '../process_es_ingest_processors_response';
|
||||
import expect from 'expect.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('processESIngestSimulateResponse', function () {
|
||||
|
||||
it('should return a list of strings indicating the enabled processors', function () {
|
||||
const response = {
|
||||
nodes: {
|
||||
node_foo: {
|
||||
ingest: {
|
||||
processors: [
|
||||
{ type: 'proc_foo' },
|
||||
{ type: 'proc_bar' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const expected = [ 'proc_foo', 'proc_bar' ];
|
||||
const actual = processESIngestProcessorsResponse(response);
|
||||
|
||||
expect(_.isEqual(actual, expected)).to.be.ok();
|
||||
});
|
||||
|
||||
it('should return a unique list of processors', function () {
|
||||
const response = {
|
||||
nodes: {
|
||||
node_foo: {
|
||||
ingest: {
|
||||
processors: [
|
||||
{ type: 'proc_foo' },
|
||||
{ type: 'proc_bar' }
|
||||
]
|
||||
}
|
||||
},
|
||||
node_bar: {
|
||||
ingest: {
|
||||
processors: [
|
||||
{ type: 'proc_foo' },
|
||||
{ type: 'proc_bar' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const expected = [ 'proc_foo', 'proc_bar' ];
|
||||
const actual = processESIngestProcessorsResponse(response);
|
||||
|
||||
expect(_.isEqual(actual, expected)).to.be.ok();
|
||||
});
|
||||
|
||||
it('should combine the available processors from all nodes', function () {
|
||||
const response = {
|
||||
nodes: {
|
||||
node_foo: {
|
||||
ingest: {
|
||||
processors: [
|
||||
{ type: 'proc_foo' }
|
||||
]
|
||||
}
|
||||
},
|
||||
node_bar: {
|
||||
ingest: {
|
||||
processors: [
|
||||
{ type: 'proc_bar' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const expected = [ 'proc_foo', 'proc_bar' ];
|
||||
const actual = processESIngestProcessorsResponse(response);
|
||||
|
||||
expect(_.isEqual(actual, expected)).to.be.ok();
|
||||
});
|
||||
|
||||
it('should return an empty array for unexpected response', function () {
|
||||
expect(_.isEqual(processESIngestProcessorsResponse({ nodes: {}}), [])).to.be.ok();
|
||||
expect(_.isEqual(processESIngestProcessorsResponse({}), [])).to.be.ok();
|
||||
expect(_.isEqual(processESIngestProcessorsResponse(undefined), [])).to.be.ok();
|
||||
expect(_.isEqual(processESIngestProcessorsResponse(null), [])).to.be.ok();
|
||||
expect(_.isEqual(processESIngestProcessorsResponse(''), [])).to.be.ok();
|
||||
expect(_.isEqual(processESIngestProcessorsResponse(1), [])).to.be.ok();
|
||||
});
|
||||
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import processESIngestSimulateError from '../process_es_ingest_simulate_error';
|
||||
import expect from 'expect.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('processESIngestSimulateError', function () {
|
||||
|
||||
it('result will be returned for processor that threw the error', function () {
|
||||
const error = _.set({}, 'body.error.root_cause[0].reason', 'foobar');
|
||||
_.set(error, 'body.error.root_cause[0].header.processor_tag', 'processor1');
|
||||
|
||||
const expected = [
|
||||
{ processorId: 'processor1', error: { compile: true, message: 'foobar' } }
|
||||
];
|
||||
const actual = processESIngestSimulateError(error);
|
||||
|
||||
expect(_.isEqual(actual, expected)).to.be.ok();
|
||||
});
|
||||
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
import processESIngestSimulateResponse from '../process_es_ingest_simulate_response';
|
||||
import expect from 'expect.js';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('processESIngestSimulateResponse', function () {
|
||||
|
||||
it('each processor that receives a result will contain response info', function () {
|
||||
const response = {
|
||||
docs: [ { processor_results: [
|
||||
{ tag: 'processor1', doc: { _source: 'new_foo' }, error: undefined },
|
||||
{ tag: 'processor2', doc: { _source: 'new_bar' }, error: undefined },
|
||||
{ tag: 'processor3', doc: { _source: 'new_baz' }, error: undefined }
|
||||
] } ]
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{ processorId: 'processor1', output: 'new_foo', error: undefined },
|
||||
{ processorId: 'processor2', output: 'new_bar', error: undefined },
|
||||
{ processorId: 'processor3', output: 'new_baz', error: undefined }
|
||||
];
|
||||
const actual = processESIngestSimulateResponse(response);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
describe('processors that return an error object', function () {
|
||||
|
||||
it('will be the root_cause reason if one exists', function () {
|
||||
const response = {
|
||||
docs: [ { processor_results: [
|
||||
{ tag: 'processor1', doc: { _source: 'new_foo' }, error: undefined },
|
||||
{
|
||||
tag: 'processor2',
|
||||
doc: 'dummy',
|
||||
error: { root_cause: [ { reason: 'something bad happened', type: 'general exception' } ] }
|
||||
}
|
||||
] } ]
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{ processorId: 'processor1', output: 'new_foo', error: undefined },
|
||||
{ processorId: 'processor2', output: undefined, error: { compile: false, message: 'something bad happened'} }
|
||||
];
|
||||
const actual = processESIngestSimulateResponse(response);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
it('will be the root_cause type if reason does not exists', function () {
|
||||
const response = {
|
||||
docs: [ { processor_results: [
|
||||
{ tag: 'processor2', doc: { _source: 'new_bar' }, error: undefined },
|
||||
{
|
||||
tag: 'processor3',
|
||||
doc: 'dummy',
|
||||
error: { root_cause: [ { type: 'something bad happened' } ] }
|
||||
}
|
||||
] } ]
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{ processorId: 'processor2', output: 'new_bar', error: undefined },
|
||||
{ processorId: 'processor3', output: undefined, error: { compile: false, message: 'something bad happened'} }
|
||||
];
|
||||
const actual = processESIngestSimulateResponse(response);
|
||||
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import * as ingestProcessorApiKibanaToEsConverters from '../processors/converters';
|
||||
|
||||
export default function ingestPipelineApiKibanaToEsConverter(pipelineApiDocument) {
|
||||
return {
|
||||
processors: _.map(pipelineApiDocument, (processor) => {
|
||||
return ingestProcessorApiKibanaToEsConverters[processor.type_id](processor);
|
||||
})
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue