From 57c391aa4e7736e8b065322dac3eb76fdb544842 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 17 Mar 2016 12:37:44 -0400 Subject: [PATCH 01/22] [Wizard] Creates a new CSV Add Data Wizard --- package.json | 1 + .../parse_csv_step/parse_csv_step.html | 57 +++++++++ .../parse_csv_step/parse_csv_step.js | 79 ++++++++++++ .../styles/_add_data_parse_csv_step.less | 64 ++++++++++ .../upload_data_step/upload_data_step.html | 22 ++++ .../upload_data_step/upload_data_step.js | 44 +++++++ .../settings/sections/indices/index.html | 7 ++ .../public/settings/sections/indices/index.js | 1 + .../indices/styles/_add_data_wizard.less | 5 + .../upload/directives/upload_wizard.html | 117 ++++++++++++++++++ .../upload/directives/upload_wizard.js | 99 +++++++++++++++ .../sections/indices/upload/index.html | 3 + .../settings/sections/indices/upload/index.js | 7 ++ .../kibana/public/settings/styles/main.less | 1 - src/ui/public/ingest/__tests__/ingest.js | 58 +++++++-- src/ui/public/ingest/ingest.js | 29 ++++- src/ui/public/styles/variables/for-theme.less | 3 + 17 files changed, 587 insertions(+), 10 deletions(-) create mode 100644 src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.html create mode 100644 src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js create mode 100644 src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/styles/_add_data_parse_csv_step.less create mode 100644 src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.html create mode 100644 src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js create mode 100644 src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html create mode 100644 src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js create mode 100644 src/plugins/kibana/public/settings/sections/indices/upload/index.html create mode 100644 src/plugins/kibana/public/settings/sections/indices/upload/index.js diff --git a/package.json b/package.json index 9249600018f1..5f620959a570 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "moment": "2.10.6", "moment-timezone": "0.4.1", "node-uuid": "1.4.7", + "papaparse": "4.1.2", "raw-loader": "0.5.1", "request": "2.61.0", "rimraf": "2.4.3", diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.html b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.html new file mode 100644 index 000000000000..75d2dc780737 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.html @@ -0,0 +1,57 @@ + +

Pick a CSV file to get started. + Please follow the instructions below. +

+ +
+
Drop your file here
+
or
+ +
Maximum upload file size: 1 GB
+
+
+ +
+

Review the sample below. + Click next if it looks like we parsed your file correctly. +

+ +
+
    +
  • {{ error }}
  • +
+
+ +
+ + + + + + + {{ wizard.file.name }} + +
+ +
+ + + + + + + + + + + +
+ {{ col | limitTo:12 }}{{ col.length > 12 ? '...' : '' }} +
{{ cell }}
+
+
diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js new file mode 100644 index 000000000000..f074aff3b717 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js @@ -0,0 +1,79 @@ +import _ from 'lodash'; +import Papa from 'papaparse'; +import modules from 'ui/modules'; +import template from './parse_csv_step.html'; +import './styles/_add_data_parse_csv_step.less'; + +modules.get('apps/settings') + .directive('parseCsvStep', function () { + return { + restrict: 'E', + template: template, + scope: { + file: '=', + parseOptions: '=', + samples: '=' + }, + bindToController: true, + controllerAs: 'wizard', + controller: function ($scope) { + + this.delimiterOptions = [ + { + label: 'comma', + value: ',' + }, + { + label: 'tab', + value: '\t' + }, + { + label: 'space', + value: ' ' + }, + { + label: 'semicolon', + value: ';' + }, + { + label: 'pipe', + value: '|' + } + ]; + + this.parse = () => { + if (!this.file) return; + + const config = _.assign( + { + header: true, + preview: 10, + dynamicTyping: true, + complete: (results) => { + $scope.$apply(() => { + this.formattedErrors = _.map(results.errors, (error) => { + return `${error.type} at row ${error.row} - ${error.message}`; + }); + this.columns = results.meta.fields; + this.rows = _.map(results.data, _.values); + this.samples = results.data; + this.parseOptions = _.defaults({}, this.parseOptions, {delimiter: results.meta.delimiter}); + }); + } + }, + this.parseOptions + ); + + Papa.parse(this.file, config); + }; + + $scope.$watch('wizard.parseOptions', this.parse, true); + $scope.$watch('wizard.file', () => { + delete this.formattedErrors; + this.parse(); + }); + + this.parse(); + } + }; + }); diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/styles/_add_data_parse_csv_step.less b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/styles/_add_data_parse_csv_step.less new file mode 100644 index 000000000000..036c4f940e43 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/styles/_add_data_parse_csv_step.less @@ -0,0 +1,64 @@ +@import (reference) "../../../styles/_add_data_wizard"; + +.upload-wizard-file-upload-container { + min-height: 300px; + display: flex; + flex-direction: column; + justify-content: center; + background-color: @settings-add-data-wizard-form-control-bg; + border: @settings-add-data-wizard-parse-csv-container-border 1px dashed; + text-align: center; + + .upload-instructions { + font-size: 2em; + } + + .upload-instructions-separator { + margin: 15px 0; + } + + button { + width: inherit; + } + + button.upload { + align-self: center; + margin-bottom: 15px; + } +} + +.upload-wizard-file-preview-container { + .preview { + overflow: auto; + max-height: 500px; + border: @settings-add-data-wizard-parse-csv-container-border 1px solid; + + table { + margin-bottom: 0; + + .table-striped() + } + } + + .parse-error { + margin-top: 2em; + } + + .advanced-options { + display: flex; + align-items: center; + + .form-group { + display: flex; + align-items: center; + padding-right: 15px; + + label { + padding-right: 8px; + margin-bottom: 0; + } + } + + padding-bottom: 10px; + } +} diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.html b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.html new file mode 100644 index 000000000000..5a92e35f403b --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.html @@ -0,0 +1,22 @@ +
+

Sit back, relax, we'll take it from here.

+ +
+ We're loading your data now. This may take some time if you selected a large file. +
+
+ + +
+

Upload complete. Let's take a look:

+ +
+ Created {{ uploadStep.created }} documents!
+
+
+ We encountered errors while indexing your data +
    +
  • {{ error }}
  • +
+
+
diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js new file mode 100644 index 000000000000..103e71568594 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js @@ -0,0 +1,44 @@ +import modules from 'ui/modules'; +import template from './upload_data_step.html'; +import _ from 'lodash'; +import IngestProvider from 'ui/ingest'; + +modules.get('apps/settings') + .directive('uploadDataStep', function () { + return { + template: template, + scope: { + results: '=' + }, + bindToController: true, + controllerAs: 'uploadStep', + controller: function ($scope, $http, Notifier, $window, Private) { + const ingest = Private(IngestProvider); + const notify = new Notifier({ + location: 'Add Data' + }); + + const usePipeline = !_.isEmpty(_.get(this.results, 'pipeline.processors')); + ingest.uploadCSV(this.results.file, this.results.indexPattern.id, this.results.parseOptions.delimiter, usePipeline) + .then( + (res) => { + this.created = 0; + this.formattedErrors = []; + _.forEach(res.data, (response) => { + this.created += response.created; + this.formattedErrors = this.formattedErrors.concat(_.map(_.get(response, 'errors.index'), (doc) => { + return `${doc._id.split('-', 1)[0].replace('L', 'Line ').trim()}: ${doc.error.type} - ${doc.error.reason}`; + })); + if (!_.isEmpty(_.get(response, 'errors.other'))) { + this.formattedErrors = this.formattedErrors.concat(response.errors.other); + } + }); + }, + (err) => { + notify.error(err); + $window.scrollTo(0, 0); + } + ); + } + }; + }); diff --git a/src/plugins/kibana/public/settings/sections/indices/index.html b/src/plugins/kibana/public/settings/sections/indices/index.html index 245753e76218..6776d0f70df5 100644 --- a/src/plugins/kibana/public/settings/sections/indices/index.html +++ b/src/plugins/kibana/public/settings/sections/indices/index.html @@ -20,6 +20,13 @@
Pick this option if you have log file data you'd like to send to Elasticsearch.
+ +

+ Upload +

+
+ Got CSVs? Upload them here. No pain, all gain. +
diff --git a/src/plugins/kibana/public/settings/sections/indices/index.js b/src/plugins/kibana/public/settings/sections/indices/index.js index e9b0012ace70..0d4c1f58a1b3 100644 --- a/src/plugins/kibana/public/settings/sections/indices/index.js +++ b/src/plugins/kibana/public/settings/sections/indices/index.js @@ -5,6 +5,7 @@ import 'plugins/kibana/settings/sections/indices/_create'; import 'plugins/kibana/settings/sections/indices/_edit'; import 'plugins/kibana/settings/sections/indices/_field_editor'; import 'plugins/kibana/settings/sections/indices/filebeat/index'; +import 'plugins/kibana/settings/sections/indices/upload/index'; import uiRoutes from 'ui/routes'; import uiModules from 'ui/modules'; import indexTemplate from 'plugins/kibana/settings/sections/indices/index.html'; diff --git a/src/plugins/kibana/public/settings/sections/indices/styles/_add_data_wizard.less b/src/plugins/kibana/public/settings/sections/indices/styles/_add_data_wizard.less index e8354811ae02..c0ebea3e4186 100644 --- a/src/plugins/kibana/public/settings/sections/indices/styles/_add_data_wizard.less +++ b/src/plugins/kibana/public/settings/sections/indices/styles/_add_data_wizard.less @@ -52,6 +52,11 @@ } } + .btn-lg { + padding: 6px 35px; + font-size: 1.2em; + } + .form-group { margin-bottom: 5px; } diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html new file mode 100644 index 000000000000..36abba36a6c8 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html @@ -0,0 +1,117 @@ +
+
+ + 1. Select + + + 2. Parse + + + 3. Review + + + 4. Upload + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ + + +
+
+ +
+
+ +
+
+
+
+ +
+ + + +
+
+ +
+
+ +
+
+
+
+ +
+ + +
+
+
+ +
+
+
+
+
+
diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js new file mode 100644 index 000000000000..4af5bff8e0d9 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js @@ -0,0 +1,99 @@ +import modules from 'ui/modules'; +import template from 'plugins/kibana/settings/sections/indices/upload/directives/upload_wizard.html'; +import IngestProvider from 'ui/ingest'; +import 'plugins/kibana/settings/sections/indices/add_data_steps/pattern_review_step'; +import 'plugins/kibana/settings/sections/indices/add_data_steps/parse_csv_step'; +import 'plugins/kibana/settings/sections/indices/add_data_steps/pipeline_setup'; +import 'plugins/kibana/settings/sections/indices/add_data_steps/upload_data_step'; + +modules.get('apps/settings') + .directive('uploadWizard', function () { + return { + restrict: 'E', + template: template, + scope: {}, + bindToController: true, + controllerAs: 'wizard', + controller: function ($scope, AppState, safeConfirm, kbnUrl, $http, Notifier, $window, config, Private) { + const ingest = Private(IngestProvider); + const $state = this.state = new AppState(); + + var notify = new Notifier({ + location: 'Add Data' + }); + + var totalSteps = 4; + this.stepResults = {}; + + this.setCurrentStep = (step) => { + if (!this.complete) { + $state.currentStep = step; + $state.save(); + } + }; + this.setCurrentStep(0); + + this.nextStep = () => { + if ($state.currentStep + 1 < totalSteps) { + this.setCurrentStep($state.currentStep + 1); + } + else if ($state.currentStep + 1 === totalSteps) { + kbnUrl.change('/discover'); + } + }; + + this.prevStep = () => { + if ($state.currentStep > 0) { + this.setCurrentStep($state.currentStep - 1); + } + }; + + this.save = () => { + const processors = this.stepResults.pipeline.processors.map(processor => processor.model); + return ingest.save(this.stepResults.indexPattern, processors) + .then( + () => { + this.nextStep(); + }, + (err) => { + notify.error(err); + $window.scrollTo(0,0); + } + ); + }; + + $scope.$watch('wizard.state.currentStep', (newValue, oldValue) => { + if (this.complete) { + $state.currentStep = totalSteps - 1; + $state.save(); + return; + } + if (newValue + 1 === totalSteps) { + this.complete = true; + } + if (newValue < oldValue) { + return safeConfirm('Going back will reset any changes you\'ve made to this step, do you want to continue?') + .then( + () => { + if ($state.currentStep < 1) { + delete this.stepResults.pipeline; + } + if ($state.currentStep < 2) { + delete this.stepResults.indexPattern; + } + this.currentStep = newValue; + }, + () => { + $state.currentStep = oldValue; + $state.save(); + } + ); + } + else { + this.currentStep = newValue; + } + }); + } + }; + }); + diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/index.html b/src/plugins/kibana/public/settings/sections/indices/upload/index.html new file mode 100644 index 000000000000..123fc4b91b11 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/upload/index.html @@ -0,0 +1,3 @@ + + + diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/index.js b/src/plugins/kibana/public/settings/sections/indices/upload/index.js new file mode 100644 index 000000000000..473dac7e7eb4 --- /dev/null +++ b/src/plugins/kibana/public/settings/sections/indices/upload/index.js @@ -0,0 +1,7 @@ +import routes from 'ui/routes'; +import template from 'plugins/kibana/settings/sections/indices/upload/index.html'; +import './directives/upload_wizard'; + +routes.when('/settings/indices/create/upload', { + template: template +}); diff --git a/src/plugins/kibana/public/settings/styles/main.less b/src/plugins/kibana/public/settings/styles/main.less index 002ea4c2f085..ab760fb42f87 100644 --- a/src/plugins/kibana/public/settings/styles/main.less +++ b/src/plugins/kibana/public/settings/styles/main.less @@ -204,4 +204,3 @@ kbn-settings-indices { .kbn-settings-indices-create { .time-and-pattern > div {} } - diff --git a/src/ui/public/ingest/__tests__/ingest.js b/src/ui/public/ingest/__tests__/ingest.js index 16dbb7ebf1c4..4ca53e5e9db3 100644 --- a/src/ui/public/ingest/__tests__/ingest.js +++ b/src/ui/public/ingest/__tests__/ingest.js @@ -26,7 +26,7 @@ describe('Ingest Service', function () { it('Sets the default index if there isn\'t one already', function () { $httpBackend - .when('POST', '../api/kibana/ingest') + .when('POST', '/api/kibana/ingest') .respond('ok'); expect(config.get('defaultIndex')).to.be(null); @@ -38,7 +38,7 @@ describe('Ingest Service', function () { it('Returns error from ingest API if there is one', function (done) { $httpBackend - .expectPOST('../api/kibana/ingest') + .expectPOST('/api/kibana/ingest') .respond(400); ingest.save({id: 'foo'}) @@ -57,7 +57,7 @@ describe('Ingest Service', function () { it('Broadcasts an ingest:updated event on the rootScope upon succesful save', function () { $httpBackend - .when('POST', '../api/kibana/ingest') + .when('POST', '/api/kibana/ingest') .respond('ok'); ingest.save({id: 'foo'}); @@ -75,7 +75,7 @@ describe('Ingest Service', function () { it('Calls the DELETE endpoint of the ingest API with the given id', function () { $httpBackend - .expectDELETE('../api/kibana/ingest/foo') + .expectDELETE('/api/kibana/ingest/foo') .respond('ok'); ingest.delete('foo'); @@ -84,7 +84,7 @@ describe('Ingest Service', function () { it('Returns error from ingest API if there is one', function (done) { $httpBackend - .expectDELETE('../api/kibana/ingest/foo') + .expectDELETE('/api/kibana/ingest/foo') .respond(404); ingest.delete('foo') @@ -103,7 +103,7 @@ describe('Ingest Service', function () { it('Broadcasts an ingest:updated event on the rootScope upon succesful save', function () { $httpBackend - .when('DELETE', '../api/kibana/ingest/foo') + .when('DELETE', '/api/kibana/ingest/foo') .respond('ok'); ingest.delete('foo'); @@ -114,11 +114,53 @@ describe('Ingest Service', function () { }); }); + describe('uploadCSV', function () { + it('throws an error if file and index pattern are not provided', function () { + expect(ingest.uploadCSV).to.throwException(/file is required/); + expect(ingest.uploadCSV).withArgs('foo').to.throwException(/index pattern is required/); + }); + + it('POSTs to the kibana _data endpoint with the correct params and the file attached as multipart/form-data', function () { + $httpBackend + .expectPOST('/api/kibana/foo/_data?csv_delimiter=;&pipeline=true', function (data) { + // The assertions we can do here are limited because of poor browser support for FormData methods + return data instanceof FormData; + }) + .respond('ok'); + + const file = new Blob(['foo,bar'], {type : 'text/csv'}); + + ingest.uploadCSV(file, 'foo', ';', true); + $httpBackend.flush(); + }); + + it('Returns error from the data API if there is one', function (done) { + $httpBackend + .expectPOST('/api/kibana/foo/_data?csv_delimiter=;&pipeline=true') + .respond(404); + + const file = new Blob(['foo,bar'], {type : 'text/csv'}); + + ingest.uploadCSV(file, 'foo', ';', true) + .then( + () => { + throw new Error('expected an error response'); + }, + (error) => { + expect(error.status).to.be(404); + done(); + } + ); + + $httpBackend.flush(); + }); + }); + describe('getProcessors', () => { it('Calls the processors GET endpoint of the ingest API', function () { $httpBackend - .expectGET('../api/kibana/ingest/processors') + .expectGET('/api/kibana/ingest/processors') .respond('ok'); ingest.getProcessors(); @@ -127,7 +169,7 @@ describe('Ingest Service', function () { it('Throws user-friendly error when there is an error in the request', function (done) { $httpBackend - .when('GET', '../api/kibana/ingest/processors') + .when('GET', '/api/kibana/ingest/processors') .respond(404); ingest.getProcessors() diff --git a/src/ui/public/ingest/ingest.js b/src/ui/public/ingest/ingest.js index bee1cb564d3a..b2d51ab93efb 100644 --- a/src/ui/public/ingest/ingest.js +++ b/src/ui/public/ingest/ingest.js @@ -1,10 +1,11 @@ import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from '../../../plugins/kibana/common/lib/case_conversion'; import _ from 'lodash'; import angular from 'angular'; +import chrome from 'ui/chrome'; export default function IngestProvider($rootScope, $http, config, $q) { - const ingestAPIPrefix = '../api/kibana/ingest'; + const ingestAPIPrefix = chrome.addBasePath('/api/kibana/ingest'); this.save = function (indexPattern, pipeline) { if (_.isEmpty(indexPattern)) { @@ -71,4 +72,30 @@ export default function IngestProvider($rootScope, $http, config, $q) { }); }; + this.uploadCSV = function (file, indexPattern, delimiter, pipeline) { + if (_.isUndefined(file)) { + throw new Error('file is required'); + } + if (_.isUndefined(indexPattern)) { + throw new Error('index pattern is required'); + } + + const formData = new FormData(); + formData.append('csv', file); + + const params = {}; + if (!_.isUndefined(delimiter)) { + params.csv_delimiter = delimiter; + } + if (!_.isUndefined(pipeline)) { + params.pipeline = pipeline; + } + + return $http.post(chrome.addBasePath(`/api/kibana/${indexPattern}/_data`), formData, { + params: params, + transformRequest: angular.identity, + headers: {'Content-Type': undefined} + }); + }; + } diff --git a/src/ui/public/styles/variables/for-theme.less b/src/ui/public/styles/variables/for-theme.less index 90a8e5f2ef5d..5d455990e37a 100644 --- a/src/ui/public/styles/variables/for-theme.less +++ b/src/ui/public/styles/variables/for-theme.less @@ -138,6 +138,9 @@ @settings-filebeat-wizard-processor-container-overlay-bg: fade(#000, 10%); +// Settings - Add Data Wizard - Parse CSV +@settings-add-data-wizard-parse-csv-container-border: @kibanaBlue3; + // Visualize =================================================================== @visualize-show-spy-border: @gray-lighter; @visualize-show-spy-bg: @white; From ed5e4e30112f2a113a71aa0b7b8a24ce71834dc2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 10 May 2016 18:58:05 -0400 Subject: [PATCH 02/22] Remove pipeline creation step from CSV wizard --- .../upload/directives/upload_wizard.html | 39 ++----------------- .../upload/directives/upload_wizard.js | 8 +--- 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html index 36abba36a6c8..4cce071eb362 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html @@ -8,17 +8,12 @@ - 2. Parse + 2. Review - 3. Review - - - 4. Upload + 3. Upload @@ -47,35 +42,9 @@
- - - -
-
- -
-
- -
-
-
-
- -
+ sample-doc="wizard.stepResults.samples[0]">
@@ -98,7 +67,7 @@
-
+
diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js index 4af5bff8e0d9..4efffe11318b 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js @@ -22,7 +22,7 @@ modules.get('apps/settings') location: 'Add Data' }); - var totalSteps = 4; + var totalSteps = 3; this.stepResults = {}; this.setCurrentStep = (step) => { @@ -49,8 +49,7 @@ modules.get('apps/settings') }; this.save = () => { - const processors = this.stepResults.pipeline.processors.map(processor => processor.model); - return ingest.save(this.stepResults.indexPattern, processors) + return ingest.save(this.stepResults.indexPattern) .then( () => { this.nextStep(); @@ -76,9 +75,6 @@ modules.get('apps/settings') .then( () => { if ($state.currentStep < 1) { - delete this.stepResults.pipeline; - } - if ($state.currentStep < 2) { delete this.stepResults.indexPattern; } this.currentStep = newValue; From e23f54adf4de5d82ec19d1a8e363f135ef274b7d Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Tue, 17 May 2016 18:18:20 -0400 Subject: [PATCH 03/22] [Wizard] Improved index pattern input validation * Made patternReviewStep a legitimate form to leverage angular validation * Started using existing index name validator directive * Enhanced said directive to optionally allow wildcards * Refactored said directive for a more canonical custom validator implementation, allowing multiple error messages to be dispalyed at once * Implemented a 'lowercase' angular form input validator --- .../settings/sections/indices/_create.html | 1 + .../pattern_review_step.html | 66 +++++++++++-------- .../pattern_review_step.js | 9 ++- .../__tests__/validate_index_name.js | 36 +++++++--- .../public/directives/validate_index_name.js | 26 +++----- .../public/directives/validate_lowercase.js | 21 ++++++ 6 files changed, 107 insertions(+), 52 deletions(-) create mode 100644 src/ui/public/directives/validate_lowercase.js diff --git a/src/plugins/kibana/public/settings/sections/indices/_create.html b/src/plugins/kibana/public/settings/sections/indices/_create.html index 7cf592c69441..fa65016ff9f2 100644 --- a/src/plugins/kibana/public/settings/sections/indices/_create.html +++ b/src/plugins/kibana/public/settings/sections/indices/_create.html @@ -62,6 +62,7 @@ ng-attr-placeholder="{{index.defaultName}}" ng-model-options="{ updateOn: 'default blur', debounce: {'default': 2500, 'blur': 0} }" validate-index-name + allow-wildcard name="name" required type="text" diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html index e10e8bf1d690..3e9115cc0c5b 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html @@ -3,31 +3,45 @@ fields can be changed if we got it wrong! -
-
-
{{ error }}
+
+
+
+
{{ error }}
+
+
+ Index names must be all lowercase +
+
+ An index name must not be empty and cannot contain whitespace or any of the following characters: ", *, \, <, |, ,, >, /, ? +
+ + + {{ reviewStep.patternInput.helpText }} + + +
- - Patterns allow you to define dynamic index names using * as a wildcard. Example: filebeat-* - - - -
- - - + + + diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js index b607c0b22026..0ff7fb30e9ad 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js @@ -6,6 +6,7 @@ import isGeoPointObject from './lib/is_geo_point_object'; import forEachField from './lib/for_each_field'; import './styles/_add_data_pattern_review_step.less'; import moment from 'moment'; +import '../../../../../../../../ui/public/directives/validate_lowercase'; function pickDefaultTimeFieldName(dateFields) { if (_.isEmpty(dateFields)) { @@ -34,6 +35,12 @@ modules.get('apps/settings') this.errors = []; const sampleFields = {}; + this.patternInput = { + label: 'Index name', + helpText: 'The name of the Elasticsearch index you want to add data to.', + defaultValue: 'data' + }; + if (_.isUndefined(this.indexPattern)) { this.indexPattern = {}; } @@ -62,7 +69,7 @@ modules.get('apps/settings') }); _.defaults(this.indexPattern, { - id: 'filebeat-*', + id: this.patternInput.defaultValue, title: 'filebeat-*', fields: _(sampleFields) .map((field, fieldName) => { diff --git a/src/ui/public/directives/__tests__/validate_index_name.js b/src/ui/public/directives/__tests__/validate_index_name.js index f792f65c8174..3deb454077e1 100644 --- a/src/ui/public/directives/__tests__/validate_index_name.js +++ b/src/ui/public/directives/__tests__/validate_index_name.js @@ -8,7 +8,8 @@ import 'ui/directives/validate_index_name'; describe('Validate index name directive', function () { let $compile; let $rootScope; - let html = ''; + let noWildcardHtml = ''; + let allowWildcardHtml = ''; beforeEach(ngMock.module('kibana')); @@ -17,14 +18,14 @@ describe('Validate index name directive', function () { $rootScope = _$rootScope_; })); - function checkPattern(input) { + function checkPattern(input, html) { $rootScope.indexName = input; let element = $compile(html)($rootScope); $rootScope.$digest(); return element; } - let badPatterns = [ + const badPatterns = [ null, undefined, '', @@ -41,19 +42,22 @@ describe('Validate index name directive', function () { 'foo,bar', ]; - let goodPatterns = [ + const goodPatterns = [ '...', 'foo', 'foo.bar', + '[foo-]YYYY-MM-DD', + ]; + + const wildcardPatterns = [ 'foo*', 'foo.bar*', - 'foo.*', - '[foo-]YYYY-MM-DD', + 'foo.*' ]; badPatterns.forEach(function (pattern) { it('should not accept index pattern: ' + pattern, function () { - let element = checkPattern(pattern); + let element = checkPattern(pattern, noWildcardHtml); expect(element.hasClass('ng-invalid')).to.be(true); expect(element.hasClass('ng-valid')).to.not.be(true); }); @@ -61,7 +65,23 @@ describe('Validate index name directive', function () { goodPatterns.forEach(function (pattern) { it('should accept index pattern: ' + pattern, function () { - let element = checkPattern(pattern); + let element = checkPattern(pattern, noWildcardHtml); + expect(element.hasClass('ng-invalid')).to.not.be(true); + expect(element.hasClass('ng-valid')).to.be(true); + }); + }); + + it('should disallow wildcards by default', function () { + wildcardPatterns.forEach(function (pattern) { + let element = checkPattern(pattern, noWildcardHtml); + expect(element.hasClass('ng-invalid')).to.be(true); + expect(element.hasClass('ng-valid')).to.not.be(true); + }); + }); + + it('should allow wildcards if the allow-wildcard attribute is present', function () { + wildcardPatterns.forEach(function (pattern) { + let element = checkPattern(pattern, allowWildcardHtml); expect(element.hasClass('ng-invalid')).to.not.be(true); expect(element.hasClass('ng-valid')).to.be(true); }); diff --git a/src/ui/public/directives/validate_index_name.js b/src/ui/public/directives/validate_index_name.js index 1082616ee0e7..10b51c4dfb8f 100644 --- a/src/ui/public/directives/validate_index_name.js +++ b/src/ui/public/directives/validate_index_name.js @@ -8,11 +8,13 @@ uiModules return { restrict: 'A', require: 'ngModel', - scope: { - 'ngModel': '=' - }, link: function ($scope, elem, attr, ngModel) { - let illegalCharacters = ['\\', '/', '?', '"', '<', '>', '|', ' ', ',']; + const illegalCharacters = ['\\', '/', '?', '"', '<', '>', '|', ' ', ',']; + const allowWildcard = !_.isUndefined(attr.allowWildcard) && attr.allowWildcard !== 'false'; + if (!allowWildcard) { + illegalCharacters.push('*'); + } + let isValid = function (input) { if (input == null || input === '' || input === '.' || input === '..') return false; @@ -22,19 +24,9 @@ uiModules return !match; }; - // From User - ngModel.$parsers.unshift(function (value) { - let valid = isValid(value); - ngModel.$setValidity('indexNameInput', valid); - return valid ? value : undefined; - }); - - // To user - ngModel.$formatters.unshift(function (value) { - ngModel.$setValidity('indexNameInput', isValid(value)); - return value; - }); - + ngModel.$validators.indexNameInput = function (modelValue, viewValue) { + return isValid(viewValue); + }; } }; }); diff --git a/src/ui/public/directives/validate_lowercase.js b/src/ui/public/directives/validate_lowercase.js new file mode 100644 index 000000000000..fb37841ee631 --- /dev/null +++ b/src/ui/public/directives/validate_lowercase.js @@ -0,0 +1,21 @@ +import uiModules from 'ui/modules'; + +uiModules +.get('kibana') +.directive('validateLowercase', function () { + return { + restrict: 'A', + require: 'ngModel', + link: function ($scope, elem, attr, ctrl) { + ctrl.$validators.lowercase = function (modelValue, viewValue) { + if (ctrl.$isEmpty(modelValue)) { + // consider empty models to be valid per lowercase rules + return true; + } + + return viewValue.toLowerCase() === viewValue; + }; + } + }; +}); + From a32364f114aa9e55d5c6e16dd0878f977569e0f2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 19 May 2016 16:50:03 -0400 Subject: [PATCH 04/22] [Wizard] Default index pattern input value on pattern review step is now configurable. --- .../pattern_review_step/pattern_review_step.js | 7 ++++++- .../sections/indices/upload/directives/upload_wizard.html | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js index 0ff7fb30e9ad..a1182a80ed5f 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.js @@ -27,7 +27,8 @@ modules.get('apps/settings') scope: { indexPattern: '=', pipeline: '=', - sampleDoc: '=' + sampleDoc: '=', + defaultIndexInput: '=' }, controllerAs: 'reviewStep', bindToController: true, @@ -41,6 +42,10 @@ modules.get('apps/settings') defaultValue: 'data' }; + if (this.defaultIndexInput) { + this.patternInput.defaultValue = this.defaultIndexInput; + } + if (_.isUndefined(this.indexPattern)) { this.indexPattern = {}; } diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html index 4cce071eb362..351e2c9f5561 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html @@ -44,6 +44,7 @@
From e11b2702dbb1763b110d8cc1418a8004c58e07af Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 19 May 2016 18:32:06 -0400 Subject: [PATCH 05/22] Refresh index pattern cache after saving or deleting via ingest service --- src/ui/public/ingest/ingest.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ui/public/ingest/ingest.js b/src/ui/public/ingest/ingest.js index b2d51ab93efb..0c3eb9093c13 100644 --- a/src/ui/public/ingest/ingest.js +++ b/src/ui/public/ingest/ingest.js @@ -1,11 +1,13 @@ +import PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider from 'plugins/kibana/settings/sections/indices/_refresh_kibana_index'; import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from '../../../plugins/kibana/common/lib/case_conversion'; import _ from 'lodash'; import angular from 'angular'; import chrome from 'ui/chrome'; -export default function IngestProvider($rootScope, $http, config, $q) { +export default function IngestProvider($rootScope, $http, config, $q, Private, indexPatterns) { const ingestAPIPrefix = chrome.addBasePath('/api/kibana/ingest'); + const refreshKibanaIndex = Private(PluginsKibanaSettingsSectionsIndicesRefreshKibanaIndexProvider); this.save = function (indexPattern, pipeline) { if (_.isEmpty(indexPattern)) { @@ -25,6 +27,10 @@ export default function IngestProvider($rootScope, $http, config, $q) { config.set('defaultIndex', indexPattern.id); } + return refreshKibanaIndex(); + }) + .then(() => { + indexPatterns.getIds.clearCache(); $rootScope.$broadcast('ingest:updated'); }); }; @@ -35,7 +41,9 @@ export default function IngestProvider($rootScope, $http, config, $q) { } return $http.delete(`${ingestAPIPrefix}/${ingestId}`) + .then(refreshKibanaIndex) .then(() => { + indexPatterns.getIds.clearCache(); $rootScope.$broadcast('ingest:updated'); }); }; From 271e78fa4c7d9da1943255ccdf49938efdde5bb5 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 19 May 2016 18:54:57 -0400 Subject: [PATCH 06/22] Enhance kbnUrl to allow setting app state on target url --- src/ui/public/url/__tests__/url.js | 42 ++++++++++++++++++++++++++++++ src/ui/public/url/url.js | 15 +++++++---- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/ui/public/url/__tests__/url.js b/src/ui/public/url/__tests__/url.js index 10319796c244..e6be67b527a6 100644 --- a/src/ui/public/url/__tests__/url.js +++ b/src/ui/public/url/__tests__/url.js @@ -272,6 +272,27 @@ describe('kbnUrl', function () { expect($location.search()).to.eql({}); expect($location.hash()).to.be(''); }); + + it('should allow setting app state on the target url', function () { + let path = '/test/path'; + let search = {search: 'test'}; + let hash = 'hash'; + let newPath = '/new/location'; + + $location.path(path).search(search).hash(hash); + + // verify the starting state + expect($location.path()).to.be(path); + expect($location.search()).to.eql(search); + expect($location.hash()).to.be(hash); + + kbnUrl.change(newPath, null, {foo: 'bar'}); + + // verify the ending state + expect($location.path()).to.be(newPath); + expect($location.search()).to.eql({_a: '(foo:bar)'}); + expect($location.hash()).to.be(''); + }); }); describe('changePath', function () { @@ -319,6 +340,27 @@ describe('kbnUrl', function () { expect($location.hash()).to.be(''); }); + it('should allow setting app state on the target url', function () { + let path = '/test/path'; + let search = {search: 'test'}; + let hash = 'hash'; + let newPath = '/new/location'; + + $location.path(path).search(search).hash(hash); + + // verify the starting state + expect($location.path()).to.be(path); + expect($location.search()).to.eql(search); + expect($location.hash()).to.be(hash); + + kbnUrl.redirect(newPath, null, {foo: 'bar'}); + + // verify the ending state + expect($location.path()).to.be(newPath); + expect($location.search()).to.eql({_a: '(foo:bar)'}); + expect($location.hash()).to.be(''); + }); + it('should replace the current history entry', function () { sinon.stub($location, 'replace'); $location.url('/some/path'); diff --git a/src/ui/public/url/url.js b/src/ui/public/url/url.js index cd3ed891e28f..e226166b97cf 100644 --- a/src/ui/public/url/url.js +++ b/src/ui/public/url/url.js @@ -2,6 +2,7 @@ import _ from 'lodash'; import 'ui/filters/uriescape'; import 'ui/filters/rison'; import uiModules from 'ui/modules'; +import rison from 'rison-node'; uiModules.get('kibana/url') @@ -17,8 +18,8 @@ function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getA * @param {Object} [paramObj] - optional set of parameters for the url template * @return {undefined} */ - self.change = function (url, paramObj) { - self._changeLocation('url', url, paramObj); + self.change = function (url, paramObj, appState) { + self._changeLocation('url', url, paramObj, false, appState); }; /** @@ -40,8 +41,8 @@ function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getA * @param {Object} [paramObj] - optional set of parameters for the url template * @return {undefined} */ - self.redirect = function (url, paramObj) { - self._changeLocation('url', url, paramObj, true); + self.redirect = function (url, paramObj, appState) { + self._changeLocation('url', url, paramObj, true, appState); }; /** @@ -142,7 +143,7 @@ function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getA ///// let reloading; - self._changeLocation = function (type, url, paramObj, replace) { + self._changeLocation = function (type, url, paramObj, replace, appState) { let prev = { path: $location.path(), search: $location.search() @@ -152,6 +153,10 @@ function KbnUrlProvider($route, $location, $rootScope, globalState, $parse, getA $location[type](url); if (replace) $location.replace(); + if (appState) { + $location.search('_a', rison.encode(appState)); + } + let next = { path: $location.path(), search: $location.search() From c080b3d50d80021c5db25d45ca78cc64688dccb2 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Thu, 19 May 2016 18:55:47 -0400 Subject: [PATCH 07/22] [Wizard] Done button now sends you to the correct index pattern on discover --- .../sections/indices/upload/directives/upload_wizard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js index 4efffe11318b..ff830285b0f7 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js @@ -38,7 +38,7 @@ modules.get('apps/settings') this.setCurrentStep($state.currentStep + 1); } else if ($state.currentStep + 1 === totalSteps) { - kbnUrl.change('/discover'); + kbnUrl.change('/discover', null, {index: this.stepResults.indexPattern.id}); } }; From 4140db8845e7b1b876972abb9747b6066e06cb5f Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 20 May 2016 17:34:11 -0400 Subject: [PATCH 08/22] [Wizard] code cleanup --- .../upload_data_step/upload_data_step.js | 74 +++++++++---------- .../filebeat/directives/filebeat_wizard.js | 2 +- .../upload/directives/upload_wizard.html | 33 +++++---- .../upload/directives/upload_wizard.js | 4 +- 4 files changed, 58 insertions(+), 55 deletions(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js index 103e71568594..f2bb175494a9 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/upload_data_step/upload_data_step.js @@ -4,41 +4,41 @@ import _ from 'lodash'; import IngestProvider from 'ui/ingest'; modules.get('apps/settings') - .directive('uploadDataStep', function () { - return { - template: template, - scope: { - results: '=' - }, - bindToController: true, - controllerAs: 'uploadStep', - controller: function ($scope, $http, Notifier, $window, Private) { - const ingest = Private(IngestProvider); - const notify = new Notifier({ - location: 'Add Data' - }); +.directive('uploadDataStep', function () { + return { + template: template, + scope: { + results: '=' + }, + bindToController: true, + controllerAs: 'uploadStep', + controller: function (Notifier, $window, Private) { + const ingest = Private(IngestProvider); + const notify = new Notifier({ + location: 'Add Data' + }); - const usePipeline = !_.isEmpty(_.get(this.results, 'pipeline.processors')); - ingest.uploadCSV(this.results.file, this.results.indexPattern.id, this.results.parseOptions.delimiter, usePipeline) - .then( - (res) => { - this.created = 0; - this.formattedErrors = []; - _.forEach(res.data, (response) => { - this.created += response.created; - this.formattedErrors = this.formattedErrors.concat(_.map(_.get(response, 'errors.index'), (doc) => { - return `${doc._id.split('-', 1)[0].replace('L', 'Line ').trim()}: ${doc.error.type} - ${doc.error.reason}`; - })); - if (!_.isEmpty(_.get(response, 'errors.other'))) { - this.formattedErrors = this.formattedErrors.concat(response.errors.other); - } - }); - }, - (err) => { - notify.error(err); - $window.scrollTo(0, 0); - } - ); - } - }; - }); + const usePipeline = !_.isEmpty(_.get(this.results, 'pipeline.processors')); + ingest.uploadCSV(this.results.file, this.results.indexPattern.id, this.results.parseOptions.delimiter, usePipeline) + .then( + (res) => { + this.created = 0; + this.formattedErrors = []; + _.forEach(res.data, (response) => { + this.created += response.created; + this.formattedErrors = this.formattedErrors.concat(_.map(_.get(response, 'errors.index'), (doc) => { + return `${doc._id.split('-', 1)[0].replace('L', 'Line ').trim()}: ${doc.error.type} - ${doc.error.reason}`; + })); + if (!_.isEmpty(_.get(response, 'errors.other'))) { + this.formattedErrors = this.formattedErrors.concat(response.errors.other); + } + }); + }, + (err) => { + notify.error(err); + $window.scrollTo(0, 0); + } + ); + } + }; +}); diff --git a/src/plugins/kibana/public/settings/sections/indices/filebeat/directives/filebeat_wizard.js b/src/plugins/kibana/public/settings/sections/indices/filebeat/directives/filebeat_wizard.js index 6de8287171bf..315bb94182ff 100644 --- a/src/plugins/kibana/public/settings/sections/indices/filebeat/directives/filebeat_wizard.js +++ b/src/plugins/kibana/public/settings/sections/indices/filebeat/directives/filebeat_wizard.js @@ -16,7 +16,7 @@ modules.get('apps/settings') scope: {}, bindToController: true, controllerAs: 'wizard', - controller: function ($scope, AppState, safeConfirm, kbnUrl, $http, Notifier, $window, config, Private) { + controller: function ($scope, AppState, safeConfirm, kbnUrl, Notifier, $window, Private) { const ingest = Private(IngestProvider); const $state = this.state = new AppState(); diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html index 351e2c9f5561..a36d8eb4fd05 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.html @@ -1,20 +1,23 @@
- - 1. Select - - - 2. Review - - - 3. Upload - + + 1. Select + + + 2. Review + + + 3. Upload +
diff --git a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js index ff830285b0f7..a485ba28ce39 100644 --- a/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js +++ b/src/plugins/kibana/public/settings/sections/indices/upload/directives/upload_wizard.js @@ -3,8 +3,8 @@ import template from 'plugins/kibana/settings/sections/indices/upload/directives import IngestProvider from 'ui/ingest'; import 'plugins/kibana/settings/sections/indices/add_data_steps/pattern_review_step'; import 'plugins/kibana/settings/sections/indices/add_data_steps/parse_csv_step'; -import 'plugins/kibana/settings/sections/indices/add_data_steps/pipeline_setup'; import 'plugins/kibana/settings/sections/indices/add_data_steps/upload_data_step'; +import '../../styles/_add_data_wizard.less'; modules.get('apps/settings') .directive('uploadWizard', function () { @@ -14,7 +14,7 @@ modules.get('apps/settings') scope: {}, bindToController: true, controllerAs: 'wizard', - controller: function ($scope, AppState, safeConfirm, kbnUrl, $http, Notifier, $window, config, Private) { + controller: function ($scope, AppState, safeConfirm, kbnUrl, Notifier, $window, Private) { const ingest = Private(IngestProvider); const $state = this.state = new AppState(); From f604b12d3fd2bdec5b465923e0bc7e09e7440f7a Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 20 May 2016 17:47:16 -0400 Subject: [PATCH 09/22] [Wizard] Ensure row values are ordered correctly --- .../indices/add_data_steps/parse_csv_step/parse_csv_step.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js index f074aff3b717..99fa45d29a35 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/parse_csv_step/parse_csv_step.js @@ -55,7 +55,11 @@ modules.get('apps/settings') return `${error.type} at row ${error.row} - ${error.message}`; }); this.columns = results.meta.fields; - this.rows = _.map(results.data, _.values); + this.rows = _.map(results.data, (row) => { + return _.map(this.columns, (columnName) => { + return row[columnName]; + }); + }); this.samples = results.data; this.parseOptions = _.defaults({}, this.parseOptions, {delimiter: results.meta.delimiter}); }); From 625ca75efd93ae3349d6ec16c5f2b6aa4d5804e3 Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Fri, 20 May 2016 18:48:30 -0400 Subject: [PATCH 10/22] [Wizard] Fix test errors caused by async task, refreshing kibana index wasn't really necessary anyway --- src/ui/public/ingest/ingest.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ui/public/ingest/ingest.js b/src/ui/public/ingest/ingest.js index 0c3eb9093c13..4d31c8271827 100644 --- a/src/ui/public/ingest/ingest.js +++ b/src/ui/public/ingest/ingest.js @@ -27,9 +27,6 @@ export default function IngestProvider($rootScope, $http, config, $q, Private, i config.set('defaultIndex', indexPattern.id); } - return refreshKibanaIndex(); - }) - .then(() => { indexPatterns.getIds.clearCache(); $rootScope.$broadcast('ingest:updated'); }); @@ -41,7 +38,6 @@ export default function IngestProvider($rootScope, $http, config, $q, Private, i } return $http.delete(`${ingestAPIPrefix}/${ingestId}`) - .then(refreshKibanaIndex) .then(() => { indexPatterns.getIds.clearCache(); $rootScope.$broadcast('ingest:updated'); From 8205f4b9a97ea9e374a2e791159426a6dfafa8dc Mon Sep 17 00:00:00 2001 From: Matthew Bargar Date: Wed, 25 May 2016 10:35:12 -0400 Subject: [PATCH 11/22] Remove default index name and add a placeholder --- .../pattern_review_step/pattern_review_step.html | 7 +++++-- .../pattern_review_step/pattern_review_step.js | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html index 3e9115cc0c5b..4816d7c9ac0f 100644 --- a/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html +++ b/src/plugins/kibana/public/settings/sections/indices/add_data_steps/pattern_review_step/pattern_review_step.html @@ -8,10 +8,12 @@
{{ error }}
-
+
Index names must be all lowercase
-
+
An index name must not be empty and cannot contain whitespace or any of the following characters: ", *, \, <, |, ,, >, /, ?
@@ -23,6 +25,7 @@ required validate-index-name validate-lowercase + placeholder="{{reviewStep.patternInput.placeholder}}" aria-describedby="pattern-help"/>