mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Merge pull request #6497 from BigFunger/ingest-pipeline-setup-client
Ingest pipeline setup client
This commit is contained in:
commit
ad349b58ae
40 changed files with 1301 additions and 105 deletions
|
@ -67,6 +67,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@bigfunger/decompress-zip": "0.2.0-stripfix2",
|
||||
"@bigfunger/jsondiffpatch": "0.1.38-webpack",
|
||||
"@spalger/angular-bootstrap": "0.12.1",
|
||||
"@spalger/filesaver": "1.1.2",
|
||||
"@spalger/leaflet-draw": "0.2.3",
|
||||
|
|
|
@ -10,4 +10,3 @@
|
|||
<div class="paste-samples">
|
||||
<textarea ng-model="pasteStep.rawSamples" placeholder="Paste your sample log lines here, separated by a newline"></textarea>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,56 +3,6 @@ const template = require('plugins/kibana/settings/sections/indices/add_data_step
|
|||
const _ = require('lodash');
|
||||
const editFieldTypeHTML = require('plugins/kibana/settings/sections/indices/partials/_edit_field_type.html');
|
||||
|
||||
const testData = {
|
||||
message: '11/24/2015 ip=1.1.1.1 bytes=1234',
|
||||
clientip: '1.1.1.1',
|
||||
bytes: 1234,
|
||||
geoip: {
|
||||
lat: 37.3894,
|
||||
lon: 122.0819
|
||||
},
|
||||
location: {
|
||||
lat: 37.3894,
|
||||
lon: 122.0819
|
||||
},
|
||||
'@timestamp': '2015-11-24T00:00:00.000Z',
|
||||
otherdate: '2015-11-24T00:00:00.000Z',
|
||||
codes: [1, 2, 3, 4]
|
||||
};
|
||||
|
||||
const testPipeline = [
|
||||
{
|
||||
grok: {
|
||||
field: 'message',
|
||||
pattern: 'foo'
|
||||
}
|
||||
},
|
||||
{
|
||||
geoip: {
|
||||
source_field: 'ip'
|
||||
}
|
||||
},
|
||||
{
|
||||
geoip: {
|
||||
source_field: 'ip',
|
||||
target_field: 'location'
|
||||
}
|
||||
},
|
||||
{
|
||||
date: {
|
||||
match_field: 'initialDate',
|
||||
match_formats: ['dd/MM/yyyy hh:mm:ss']
|
||||
}
|
||||
},
|
||||
{
|
||||
date: {
|
||||
match_field: 'initialDate',
|
||||
match_formats: ['dd/MM/yyyy hh:mm:ss'],
|
||||
target_field: 'otherdate'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function pickDefaultTimeFieldName(dateFields) {
|
||||
if (_.isEmpty(dateFields)) {
|
||||
return undefined;
|
||||
|
@ -66,29 +16,26 @@ modules.get('apps/settings')
|
|||
return {
|
||||
template: template,
|
||||
scope: {
|
||||
sampleDocs: '=',
|
||||
indexPattern: '=',
|
||||
pipeline: '='
|
||||
pipeline: '=',
|
||||
sampleDoc: '='
|
||||
},
|
||||
controllerAs: 'reviewStep',
|
||||
bindToController: true,
|
||||
controller: function ($scope, Private) {
|
||||
this.sampleDocs = testData;
|
||||
this.pipeline = testPipeline;
|
||||
|
||||
if (_.isUndefined(this.indexPattern)) {
|
||||
this.indexPattern = {};
|
||||
}
|
||||
|
||||
const knownFieldTypes = {};
|
||||
this.dateFields = [];
|
||||
this.pipeline.forEach((processor) => {
|
||||
if (processor.geoip) {
|
||||
const field = processor.geoip.target_field || 'geoip';
|
||||
this.pipeline.model.processors.forEach((processor) => {
|
||||
if (processor.typeId === 'geoip') {
|
||||
const field = processor.targetField || 'geoip';
|
||||
knownFieldTypes[field] = 'geo_point';
|
||||
}
|
||||
else if (processor.date) {
|
||||
const field = processor.date.target_field || '@timestamp';
|
||||
else if (processor.typeId === 'date') {
|
||||
const field = processor.targetField || '@timestamp';
|
||||
knownFieldTypes[field] = 'date';
|
||||
this.dateFields.push(field);
|
||||
}
|
||||
|
@ -98,7 +45,7 @@ modules.get('apps/settings')
|
|||
id: 'filebeat-*',
|
||||
title: 'filebeat-*',
|
||||
timeFieldName: pickDefaultTimeFieldName(this.dateFields),
|
||||
fields: _.map(this.sampleDocs, (value, key) => {
|
||||
fields: _.map(this.sampleDoc, (value, key) => {
|
||||
let type = knownFieldTypes[key] || typeof value;
|
||||
if (type === 'object' && _.isArray(value) && !_.isEmpty(value)) {
|
||||
type = typeof value[0];
|
||||
|
@ -126,7 +73,7 @@ modules.get('apps/settings')
|
|||
|
||||
const buildRows = () => {
|
||||
this.rows = _.map(this.indexPattern.fields, (field) => {
|
||||
const sampleValue = this.sampleDocs[field.name];
|
||||
const sampleValue = this.sampleDoc[field.name];
|
||||
return [
|
||||
_.escape(field.name),
|
||||
{
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import jsondiffpatch from '@bigfunger/jsondiffpatch';
|
||||
import '../styles/_output_preview.less';
|
||||
|
||||
const htmlFormat = jsondiffpatch.formatters.html.format;
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('outputPreview', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('../views/output_preview.html'),
|
||||
scope: {
|
||||
oldObject: '=',
|
||||
newObject: '='
|
||||
},
|
||||
link: function ($scope, $el) {
|
||||
const div = $el.find('.visual')[0];
|
||||
|
||||
$scope.diffpatch = jsondiffpatch.create({
|
||||
arrays: {
|
||||
detectMove: false
|
||||
},
|
||||
textDiff: {
|
||||
minLength: 120
|
||||
}
|
||||
});
|
||||
|
||||
$scope.updateUi = function () {
|
||||
const left = $scope.oldObject;
|
||||
const right = $scope.newObject;
|
||||
let delta = $scope.diffpatch.diff(left, right);
|
||||
if (!delta) delta = {};
|
||||
|
||||
div.innerHTML = htmlFormat(delta, left);
|
||||
};
|
||||
},
|
||||
controller: function ($scope, debounce) {
|
||||
$scope.collapsed = true;
|
||||
|
||||
const updateOutput = debounce(function () {
|
||||
$scope.updateUi();
|
||||
}, 200);
|
||||
|
||||
$scope.$watch('oldObject', updateOutput);
|
||||
$scope.$watch('newObject', updateOutput);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import '../styles/_pipeline_output.less';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('pipelineOutput', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('../views/pipeline_output.html'),
|
||||
scope: {
|
||||
pipeline: '='
|
||||
},
|
||||
controller: function ($scope) {
|
||||
$scope.collapsed = true;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import _ from 'lodash';
|
||||
import Pipeline from '../lib/pipeline';
|
||||
import angular from 'angular';
|
||||
import * as ProcessorTypes from '../lib/processor_types';
|
||||
import IngestProvider from 'ui/ingest';
|
||||
import '../styles/_pipeline_setup.less';
|
||||
import './pipeline_output';
|
||||
import './source_data';
|
||||
import './processor_ui';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
function buildProcessorTypeList() {
|
||||
return _(ProcessorTypes)
|
||||
.map(Type => {
|
||||
const instance = new Type();
|
||||
return {
|
||||
typeId: instance.typeId,
|
||||
title: instance.title,
|
||||
Type
|
||||
};
|
||||
})
|
||||
.compact()
|
||||
.value();
|
||||
}
|
||||
|
||||
app.directive('pipelineSetup', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('../views/pipeline_setup.html'),
|
||||
scope: {
|
||||
samples: '=',
|
||||
pipeline: '='
|
||||
},
|
||||
controller: function ($scope, debounce, Private, Notifier) {
|
||||
const ingest = Private(IngestProvider);
|
||||
const notify = new Notifier({ location: `Ingest Pipeline Setup` });
|
||||
$scope.processorTypes = _.sortBy(buildProcessorTypeList(), 'title');
|
||||
$scope.sample = {};
|
||||
|
||||
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.dirty) return;
|
||||
|
||||
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('pipeline.dirty', simulatePipeline);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
import './processor_ui_container';
|
||||
import './processor_ui_set';
|
|
@ -0,0 +1,33 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import _ from 'lodash';
|
||||
import '../styles/_processor_ui_container.less';
|
||||
import './output_preview';
|
||||
import './processor_ui_container_header';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('processorUiContainer', function ($compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
pipeline: '=',
|
||||
processor: '='
|
||||
},
|
||||
template: require('../views/processor_ui_container.html'),
|
||||
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);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import '../styles/_processor_ui_container_header.less';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('processorUiContainerHeader', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
processor: '=',
|
||||
field: '=',
|
||||
pipeline: '='
|
||||
},
|
||||
template: require('../views/processor_ui_container_header.html')
|
||||
};
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import uiModules from 'ui/modules';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
//scope.processor, scope.pipeline are attached by the process_container.
|
||||
app.directive('processorUiSet', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: require('../views/processor_ui_set.html'),
|
||||
controller : function ($scope) {
|
||||
const processor = $scope.processor;
|
||||
const pipeline = $scope.pipeline;
|
||||
|
||||
function processorUiChanged() {
|
||||
pipeline.dirty = true;
|
||||
}
|
||||
|
||||
$scope.$watch('processor.targetField', processorUiChanged);
|
||||
$scope.$watch('processor.value', processorUiChanged);
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import angular from 'angular';
|
||||
import '../styles/_source_data.less';
|
||||
|
||||
const app = uiModules.get('kibana');
|
||||
|
||||
app.directive('sourceData', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
samples: '=',
|
||||
sample: '='
|
||||
},
|
||||
template: require('../views/source_data.html'),
|
||||
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) return;
|
||||
|
||||
$scope.selectedSample = samples[currentIndex - 1];
|
||||
};
|
||||
|
||||
$scope.nextLine = function () {
|
||||
let currentIndex = samples.indexOf($scope.selectedSample);
|
||||
if (currentIndex >= samples.length - 1) return;
|
||||
|
||||
$scope.selectedSample = samples[currentIndex + 1];
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
import './directives/pipeline_setup';
|
|
@ -1,7 +1,6 @@
|
|||
var expect = require('expect.js');
|
||||
var sinon = require('sinon');
|
||||
|
||||
var keysDeep = require('../keys_deep');
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import keysDeep from '../keys_deep';
|
||||
|
||||
describe('keys deep', function () {
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
import _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import Pipeline from '../../lib/pipeline';
|
||||
|
||||
describe('processor pipeline', function () {
|
||||
|
||||
class TestProcessor {
|
||||
constructor(processorId) {
|
||||
this.processorId = processorId;
|
||||
}
|
||||
|
||||
setParent(newParent) { }
|
||||
}
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.processors[0].model = { bar: 'baz' };
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
const oldProcessors = [ pipeline.processors[0], pipeline.processors[1], pipeline.processors[2] ];
|
||||
|
||||
const newPipeline = new Pipeline();
|
||||
newPipeline.add(TestProcessor);
|
||||
newPipeline.add(TestProcessor);
|
||||
newPipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
newPipeline.add(TestProcessor);
|
||||
newPipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
expect(pipeline.processors.length).to.be(3);
|
||||
});
|
||||
|
||||
it('should append assign each new processor a unique processorId', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
expect(pipeline.processors[0] instanceof TestProcessor).to.be(true);
|
||||
expect(pipeline.processors[1] instanceof TestProcessor).to.be(true);
|
||||
expect(pipeline.processors[2] instanceof TestProcessor).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 TestProcessor();
|
||||
testProcessor.processorId = 'foo';
|
||||
testProcessor.foo = 'bar';
|
||||
testProcessor.bar = 'baz';
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors.length).to.be(1);
|
||||
});
|
||||
|
||||
it('should instanciate an object of the same class as the object passed in', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
const testProcessor = new TestProcessor();
|
||||
testProcessor.processorId = 'foo';
|
||||
testProcessor.foo = 'bar';
|
||||
testProcessor.bar = 'baz';
|
||||
|
||||
pipeline.addExisting(testProcessor);
|
||||
|
||||
expect(pipeline.processors[0] instanceof TestProcessor).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 TestProcessor();
|
||||
testProcessor.processorId = 'foo';
|
||||
testProcessor.foo = 'bar';
|
||||
testProcessor.bar = 'baz';
|
||||
|
||||
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 TestProcessor();
|
||||
testProcessor.processorId = '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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
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(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
pipeline.add(TestProcessor);
|
||||
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 output to be last processors output if processors exist', function () {
|
||||
const pipeline = new Pipeline();
|
||||
pipeline.add(TestProcessor);
|
||||
|
||||
const expected = { foo: 'bar' };
|
||||
pipeline.processors[0].outputObject = expected;
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.be(expected);
|
||||
});
|
||||
|
||||
it('should set output to be undefined if no processors exist', function () {
|
||||
const pipeline = new Pipeline();
|
||||
|
||||
pipeline.updateOutput();
|
||||
expect(pipeline.output).to.be(undefined);
|
||||
});
|
||||
|
||||
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,4 +1,4 @@
|
|||
const _ = require('lodash');
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function keysDeep(object, base) {
|
||||
let result = [];
|
|
@ -0,0 +1,142 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
export default class Pipeline {
|
||||
|
||||
constructor() {
|
||||
this.processors = [];
|
||||
this.counter = 0;
|
||||
this.input = {};
|
||||
this.output = undefined;
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
get model() {
|
||||
return {
|
||||
input: this.input,
|
||||
processors: _.map(this.processors, processor => processor.model)
|
||||
};
|
||||
}
|
||||
|
||||
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.counter += 1;
|
||||
const processorId = `processor_${this.counter}`;
|
||||
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;
|
||||
}
|
||||
|
||||
updateOutput() {
|
||||
const processors = this.processors;
|
||||
|
||||
this.output = undefined;
|
||||
if (processors.length > 0) {
|
||||
this.output = processors[processors.length - 1].outputObject;
|
||||
}
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
getProcessorById(processorId) {
|
||||
const result = _.find(this.processors, { processorId });
|
||||
|
||||
if (!result) {
|
||||
throw new Error(`Could not find processor by id [${processorId}]`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Updates the state of the pipeline and processors with the results
|
||||
// from an ingest simulate call.
|
||||
applySimulateResults(results) {
|
||||
//update the outputObject of each processor
|
||||
results.forEach((result) => {
|
||||
const processor = this.getProcessorById(result.processorId);
|
||||
|
||||
processor.outputObject = _.get(result, 'output');
|
||||
processor.error = _.get(result, 'error');
|
||||
});
|
||||
|
||||
//update the inputObject of each processor
|
||||
results.forEach((result) => {
|
||||
const processor = this.getProcessorById(result.processorId);
|
||||
|
||||
//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, 'error.isNested')) {
|
||||
//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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.updateOutput();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
output-preview {
|
||||
.visual {
|
||||
background-color: @settings-pipeline-setup-output-preview-bg;
|
||||
border: 1px solid;
|
||||
border-color: @settings-pipeline-setup-output-preview-border;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.visual.collapsed {
|
||||
max-height: 125px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.hide-unchanged {
|
||||
.jsondiffpatch-unchanged {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
pipeline-output {
|
||||
display: block;
|
||||
|
||||
.header-line {
|
||||
display: flex;
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
pre.collapsed {
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
pipeline-setup {
|
||||
label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
ul.pipeline-container {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
|
||||
&>li {
|
||||
border: 1px solid;
|
||||
border-color: @settings-pipeline-setup-pipeline-border;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-pipeline {
|
||||
border: 1px solid;
|
||||
border-color: @settings-pipeline-setup-pipeline-border;
|
||||
padding: 5px;
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
@import (reference) "~ui/styles/mixins";
|
||||
@import (reference) "~ui/styles/theme";
|
||||
|
||||
processor-ui-container {
|
||||
display: block;
|
||||
margin-bottom: 1px;
|
||||
|
||||
.processor-ui-container-body {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&-content {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
||||
top: -5000px;
|
||||
left: -5000px;
|
||||
width: 10000px;
|
||||
height: 10000px;
|
||||
background-color: @settings-pipeline-setup-processor-container-overlay-bg;
|
||||
}
|
||||
|
||||
&.dirty {
|
||||
.overlay {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
@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-pipeline-setup-processor-container-overlay-bg;
|
||||
border-bottom: 1px solid;
|
||||
border-bottom-color: @settings-pipeline-setup-processor-container-header-border;
|
||||
|
||||
&-toggle {
|
||||
flex: 0 0 auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&-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;
|
||||
}
|
||||
}
|
||||
|
||||
&-controls {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
processor-ui-date {
|
||||
.custom-date-format {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
source-data {
|
||||
button {
|
||||
width: 40px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
div.controls {
|
||||
display: flex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<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>
|
|
@ -0,0 +1,12 @@
|
|||
<div class="form-group">
|
||||
<div class="header-line">
|
||||
<label>Pipeline Output:</label>
|
||||
<a
|
||||
ng-click="collapsed = false"
|
||||
ng-show="collapsed">expand</a>
|
||||
<a
|
||||
ng-click="collapsed = true"
|
||||
ng-hide="collapsed">collapse</a>
|
||||
</div>
|
||||
<pre class="output" ng-class="{ collapsed: collapsed }">{{ pipeline.output | json }}</pre>
|
||||
</div>
|
|
@ -0,0 +1,33 @@
|
|||
<source-data sample="sample" samples="samples"></source-data>
|
||||
|
||||
<pipeline-output pipeline="pipeline"></pipeline-output>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Processor Pipeline:</label>
|
||||
<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="empty-pipeline"
|
||||
ng-hide="pipeline.processors.length > 0">
|
||||
<p>Your pipeline is currently empty. Add a processor to get started!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Processor Type:</label>
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="processorType.title for processorType in processorTypes"
|
||||
ng-model="processorType">
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
ng-click="pipeline.add(processorType.Type)"
|
||||
ng-disabled="!processorType">
|
||||
Add Processor
|
||||
</button>
|
|
@ -0,0 +1,21 @@
|
|||
<processor-ui-container-header
|
||||
processor="processor"
|
||||
field="sourceField"
|
||||
pipeline="pipeline">
|
||||
</processor-ui-container-header>
|
||||
<div
|
||||
class="processor-ui-container-body"
|
||||
ng-class="{dirty: processor.error.isNested}">
|
||||
<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"></output-preview>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</div>
|
|
@ -0,0 +1,58 @@
|
|||
<div class="processor-ui-container-header">
|
||||
<button
|
||||
aria-label="{{ processor.collapsed ? 'Open Editor' : 'Close Editor' }}"
|
||||
tooltip="{{ processor.collapsed ? 'Open Editor' : 'Close Editor' }}"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="processor.collapsed = !processor.collapsed"
|
||||
type="button"
|
||||
class="btn btn-default 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-default">
|
||||
<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-default">
|
||||
<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">
|
||||
<i aria-hidden="true" class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
<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>
|
|
@ -0,0 +1,28 @@
|
|||
<div class="form-group">
|
||||
<label>Current Line:</label>
|
||||
<div class="controls">
|
||||
<select
|
||||
class="form-control"
|
||||
ng-options="sample.message for sample in samples"
|
||||
ng-model="selectedSample">
|
||||
</select>
|
||||
<button
|
||||
aria-label="Previous Line"
|
||||
tooltip="Previous Line"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="previousLine()"
|
||||
type="button"
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Next Line"
|
||||
tooltip="Next Line"
|
||||
tooltip-append-to-body="true"
|
||||
ng-click="nextLine()"
|
||||
type="button"
|
||||
class="btn btn-xs btn-default">
|
||||
<i aria-hidden="true" class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||
<h2>Build pipeline step</h2>
|
||||
|
||||
<div>
|
||||
Logs: {{samples}}
|
||||
</div>
|
||||
|
||||
<button ng-click="sampleDocs = {results: {os: 'osx'}}; pipeline = {processor: 'I processor'};">Build a pipeline</button>
|
|
@ -1,15 +0,0 @@
|
|||
var modules = require('ui/modules');
|
||||
var template = require('plugins/kibana/settings/sections/indices/add_data_steps/pipeline_step.html');
|
||||
|
||||
modules.get('apps/settings')
|
||||
.directive('pipelineStep', function () {
|
||||
return {
|
||||
template: template,
|
||||
scope: {
|
||||
samples: '=',
|
||||
sampleDocs: '=',
|
||||
pipeline: '='
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -38,23 +38,22 @@
|
|||
</div>
|
||||
|
||||
<div ng-switch-when="1">
|
||||
<pipeline-step
|
||||
samples="wizard.stepResults.samples"
|
||||
<pipeline-setup
|
||||
pipeline="wizard.stepResults.pipeline"
|
||||
sample-docs="wizard.stepResults.sampleDocs">
|
||||
</pipeline-step>
|
||||
samples="wizard.stepResults.samples">
|
||||
</pipeline-setup>
|
||||
|
||||
<div class="nav-buttons">
|
||||
<button ng-click="wizard.prevStep()">Prev</button>
|
||||
<button ng-disabled="!wizard.stepResults.pipeline || !wizard.stepResults.sampleDocs" ng-click="wizard.nextStep()">Next</button>
|
||||
<button ng-disabled="!wizard.stepResults.pipeline" ng-click="wizard.nextStep()">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-switch-when="2">
|
||||
<pattern-review-step
|
||||
index-pattern="wizard.stepResults.indexPattern"
|
||||
sample-docs="wizard.stepResults.sampleDocs"
|
||||
pipeline="wizard.stepResults.pipeline">
|
||||
pipeline="wizard.stepResults.pipeline"
|
||||
sample-doc="wizard.stepResults.pipeline.output">
|
||||
</pattern-review-step>
|
||||
|
||||
<div class="nav-buttons">
|
||||
|
|
|
@ -4,7 +4,7 @@ import IngestProvider from 'ui/ingest';
|
|||
|
||||
require('plugins/kibana/settings/sections/indices/add_data_steps/pattern_review_step');
|
||||
require('plugins/kibana/settings/sections/indices/add_data_steps/paste_samples_step');
|
||||
require('plugins/kibana/settings/sections/indices/add_data_steps/pipeline_step');
|
||||
require('plugins/kibana/settings/sections/indices/add_data_steps/pipeline_setup');
|
||||
require('plugins/kibana/settings/sections/indices/add_data_steps/install_filebeat_step');
|
||||
|
||||
// wrapper directive, which sets up the breadcrumb for all filebeat steps
|
||||
|
@ -51,7 +51,8 @@ modules.get('apps/settings')
|
|||
};
|
||||
|
||||
this.save = () => {
|
||||
return ingest.save(this.stepResults.indexPattern, this.stepResults.pipeline)
|
||||
const processors = this.stepResults.pipeline.processors.map(processor => processor.model);
|
||||
return ingest.save(this.stepResults.indexPattern, processors)
|
||||
.then(
|
||||
() => {
|
||||
this.nextStep();
|
||||
|
|
|
@ -180,6 +180,10 @@ kbn-settings-indices {
|
|||
p.text-center {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.nav-buttons {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ import Joi from 'joi';
|
|||
|
||||
const base = Joi.object({
|
||||
processor_id: Joi.string().required()
|
||||
}).unknown();
|
||||
});
|
||||
|
||||
export const set = base.keys({
|
||||
type_id: Joi.string().only('set').required(),
|
||||
target_field: Joi.string().required(),
|
||||
value: Joi.any().required()
|
||||
target_field: Joi.string().allow(''),
|
||||
value: Joi.string().allow('')
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { keysToSnakeCaseShallow } from '../../../plugins/kibana/common/lib/case_conversion';
|
||||
import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from '../../../plugins/kibana/common/lib/case_conversion';
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
|
||||
export default function IngestProvider($rootScope, $http, config) {
|
||||
|
||||
|
@ -14,7 +15,7 @@ export default function IngestProvider($rootScope, $http, config) {
|
|||
index_pattern: keysToSnakeCaseShallow(indexPattern)
|
||||
};
|
||||
if (!_.isEmpty(pipeline)) {
|
||||
payload.pipeline = pipeline;
|
||||
payload.pipeline = _.map(pipeline, processor => keysToSnakeCaseShallow(processor));
|
||||
}
|
||||
|
||||
return $http.post(`${ingestAPIPrefix}`, payload)
|
||||
|
@ -38,4 +39,24 @@ export default function IngestProvider($rootScope, $http, config) {
|
|||
});
|
||||
};
|
||||
|
||||
this.simulate = function (pipeline) {
|
||||
function pack(pipeline) {
|
||||
const result = keysToSnakeCaseShallow(pipeline);
|
||||
result.processors = _.map(result.processors, processor => keysToSnakeCaseShallow(processor));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function unpack(response) {
|
||||
const data = response.data.map(result => keysToCamelCaseShallow(result));
|
||||
return data;
|
||||
}
|
||||
|
||||
return $http.post(`${ingestAPIPrefix}/simulate`, pack(pipeline))
|
||||
.then(unpack)
|
||||
.catch(err => {
|
||||
throw ('Error communicating with Kibana server');
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -121,6 +121,14 @@
|
|||
|
||||
@settings-indices-active-color: @btn-success-bg;
|
||||
|
||||
// Settings - Add Data Wizard - Pipeline Setup =================================
|
||||
@settings-pipeline-setup-output-preview-border: @gray12;
|
||||
@settings-pipeline-setup-output-preview-bg: @gray-lighter;
|
||||
|
||||
@settings-pipeline-setup-pipeline-border: @gray12;
|
||||
|
||||
@settings-pipeline-setup-processor-container-overlay-bg: fade(#000, 10%);
|
||||
@settings-pipeline-setup-processor-container-header-border: @gray12;
|
||||
|
||||
// Visualize ===================================================================
|
||||
@visualize-show-spy-border: @gray-lighter;
|
||||
|
|
|
@ -25,7 +25,8 @@ define(function (require) {
|
|||
processors: [{
|
||||
processor_id: 'processor1',
|
||||
type_id: 'set',
|
||||
value: 'bar'
|
||||
value: 'bar',
|
||||
target_field: 42
|
||||
}]
|
||||
})
|
||||
.expect(400)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue