From cb547f2f2062f866462e167c26804cf6fcb67869 Mon Sep 17 00:00:00 2001 From: Joe Fleming Date: Tue, 24 Mar 2015 15:26:12 -0700 Subject: [PATCH 1/3] use a promise chain to handle multiple calls to updateSamples --- .../settings/sections/indices/_create.js | 121 +++++++++++------- 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/src/kibana/plugins/settings/sections/indices/_create.js b/src/kibana/plugins/settings/sections/indices/_create.js index e6cb4b00d677..253d65b46784 100644 --- a/src/kibana/plugins/settings/sections/indices/_create.js +++ b/src/kibana/plugins/settings/sections/indices/_create.js @@ -12,11 +12,12 @@ define(function (require) { }); require('modules').get('apps/settings') - .controller('settingsIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config) { + .controller('settingsIndicesCreate', function ($scope, kbnUrl, Private, Notifier, indexPatterns, es, config, Promise) { var notify = new Notifier(); var MissingIndices = errors.IndexPatternMissingIndices; var refreshKibanaIndex = Private(require('plugins/settings/sections/indices/_refresh_kibana_index')); var intervals = indexPatterns.intervals; + var samplePromise; // this and child scopes will write pattern vars here var index = $scope.index = { @@ -33,45 +34,6 @@ define(function (require) { index.nameInterval = _.find(index.nameIntervalOptions, { name: 'daily' }); index.timeField = null; - var updateSamples = function () { - index.samples = null; - index.existing = null; - index.patternErrors = []; - - if (!index.nameInterval || !index.name) { - return; - } - - var pattern = mockIndexPattern(index); - - indexPatterns.mapper.getIndicesForIndexPattern(pattern) - .then(function (existing) { - var all = existing.all; - var matches = existing.matches; - if (all.length) { - index.existing = { - class: 'success', - all: all, - matches: matches, - matchPercent: Math.round((matches.length / all.length) * 100) + '%', - failures: _.difference(all, matches) - }; - return; - } - - index.patternErrors.push('Pattern does not match any existing indices'); - var radius = Math.round(index.sampleCount / 2); - var samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius); - - if (_.uniq(samples).length !== samples.length) { - index.patternErrors.push('Invalid pattern, interval does not create unique index names'); - } else { - index.samples = samples; - } - }) - .catch(notify.error); - }; - $scope.refreshFieldList = function () { index.dateFields = index.timeField = index.listUsed = null; var useIndexList = index.isTimeBased && index.nameIsPattern; @@ -194,7 +156,6 @@ define(function (require) { index.nameInterval = index.nameInterval || intervals.byName['days']; index.name = index.name || getPatternDefault(index.nameInterval); } - }); var mockIndexPattern = function (index) { @@ -213,13 +174,85 @@ define(function (require) { $scope.$watchMulti([ 'index.name', 'index.nameInterval' - ], updateSamples); + ], function (newVal, oldVal) { + var lastPromise; + samplePromise = lastPromise = updateSamples() + .then(function () { + promiseMatch(lastPromise, function () { + index.samples = null; + index.patternErrors = []; + }); + }) + .catch(function (errors) { + promiseMatch(lastPromise, function () { + index.existing = null; + index.patternErrors = errors; + }); + }) + .finally(function () { + // prevent running when no change happened (ie, first watcher call) + if (!_.isEqual(newVal, oldVal)) { + promiseMatch(lastPromise, function () { + $scope.refreshFieldList(); + samplePromise = Promise.resolve(); + }); + } + }); + }); $scope.$watchMulti([ - 'index.name', 'index.isTimeBased', - 'index.nameInterval', 'index.sampleCount' ], $scope.refreshFieldList); + + function updateSamples() { + var patternErrors = []; + + if (!index.nameInterval || !index.name) { + return Promise.resolve(); + } + + var pattern = mockIndexPattern(index); + + return indexPatterns.mapper.getIndicesForIndexPattern(pattern) + .catch(notify.error) + .then(function (existing) { + var all = existing.all; + var matches = existing.matches; + if (all.length) { + index.existing = { + class: 'success', + all: all, + matches: matches, + matchPercent: Math.round((matches.length / all.length) * 100) + '%', + failures: _.difference(all, matches) + }; + return; + } + + patternErrors.push('Pattern does not match any existing indices'); + var radius = Math.round(index.sampleCount / 2); + var samples = intervals.toIndexList(index.name, index.nameInterval, -radius, radius); + + if (_.uniq(samples).length !== samples.length) { + patternErrors.push('Invalid pattern, interval does not create unique index names'); + } else { + index.samples = samples; + } + + throw patternErrors; + }); + } + + function promiseMatch(lastPromise, cb) { + if (lastPromise === samplePromise) { + cb(); + } else { + index.patternErrors = []; + index.samples = null; + index.existing = null; + index.fetchFieldsError = 'Loading'; + } + } }); }); From 0438874967d71f7447d8ac6f4c7f46eb88b9f35f Mon Sep 17 00:00:00 2001 From: Joe Fleming Date: Tue, 24 Mar 2015 15:28:45 -0700 Subject: [PATCH 2/3] use function hoisting for utility functions --- .../settings/sections/indices/_create.js | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/kibana/plugins/settings/sections/indices/_create.js b/src/kibana/plugins/settings/sections/indices/_create.js index 253d65b46784..c37ad54eed51 100644 --- a/src/kibana/plugins/settings/sections/indices/_create.js +++ b/src/kibana/plugins/settings/sections/indices/_create.js @@ -118,24 +118,6 @@ define(function (require) { 'index.nameIsPattern', 'index.nameInterval.name' ], function (newVal, oldVal) { - - function getPatternDefault(interval) { - switch (interval) { - case 'hours': - return '[logstash-]YYYY.MM.DD.HH'; - case 'days': - return '[logstash-]YYYY.MM.DD'; - case 'weeks': - return '[logstash-]GGGG.WW'; - case 'months': - return '[logstash-]YYYY.MM'; - case 'years': - return '[logstash-]YYYY'; - default: - return 'logstash-*'; - } - } - var isTimeBased = newVal[0]; var nameIsPattern = newVal[1]; var newDefault = getPatternDefault(newVal[2]); @@ -158,14 +140,6 @@ define(function (require) { } }); - var mockIndexPattern = function (index) { - // trick the mapper into thinking this is an indexPattern - return { - id: index.name, - intervalName: index.nameInterval - }; - }; - $scope.moreSamples = function (andUpdate) { index.sampleCount += 5; if (andUpdate) updateSamples(); @@ -254,5 +228,30 @@ define(function (require) { index.fetchFieldsError = 'Loading'; } } + + function getPatternDefault(interval) { + switch (interval) { + case 'hours': + return '[logstash-]YYYY.MM.DD.HH'; + case 'days': + return '[logstash-]YYYY.MM.DD'; + case 'weeks': + return '[logstash-]GGGG.WW'; + case 'months': + return '[logstash-]YYYY.MM'; + case 'years': + return '[logstash-]YYYY'; + default: + return 'logstash-*'; + } + } + + function mockIndexPattern(index) { + // trick the mapper into thinking this is an indexPattern + return { + id: index.name, + intervalName: index.nameInterval + }; + } }); }); From fa6852e3f26f26136d989fd1793029373dbdf73e Mon Sep 17 00:00:00 2001 From: Joe Fleming Date: Thu, 26 Mar 2015 11:53:54 -0700 Subject: [PATCH 3/3] fix refreshFieldList race condition and flicker issue --- .../settings/sections/indices/_create.js | 100 +++++++++++------- 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/src/kibana/plugins/settings/sections/indices/_create.js b/src/kibana/plugins/settings/sections/indices/_create.js index c37ad54eed51..6db1de9e6a24 100644 --- a/src/kibana/plugins/settings/sections/indices/_create.js +++ b/src/kibana/plugins/settings/sections/indices/_create.js @@ -35,43 +35,7 @@ define(function (require) { index.timeField = null; $scope.refreshFieldList = function () { - index.dateFields = index.timeField = index.listUsed = null; - var useIndexList = index.isTimeBased && index.nameIsPattern; - - // we don't have enough info to continue - if (!index.name) { - index.fetchFieldsError = 'Set an index name first'; - return; - } - - if (useIndexList && !index.nameInterval) { - index.fetchFieldsError = 'Select the interval at which your indices are populated.'; - return; - } - - return indexPatterns.mapper.clearCache(index.name) - .then(function () { - var pattern = mockIndexPattern(index); - - return indexPatterns.mapper.getFieldsForIndexPattern(pattern, true) - .catch(function (err) { - // TODO: we should probably display a message of some kind - if (err instanceof MissingIndices) { - index.fetchFieldsError = 'Unable to fetch mapping. Do you have indices matching the pattern?'; - return []; - } - - throw err; - }); - }) - .then(function (fields) { - if (fields.length > 0) { - index.fetchFieldsError = null; - index.dateFields = fields.filter(function (field) { - return field.type === 'date'; - }); - } - }, notify.fatal); + fetchFieldList().then(updateFieldList); }; $scope.createIndexPattern = function () { @@ -166,9 +130,10 @@ define(function (require) { .finally(function () { // prevent running when no change happened (ie, first watcher call) if (!_.isEqual(newVal, oldVal)) { - promiseMatch(lastPromise, function () { - $scope.refreshFieldList(); - samplePromise = Promise.resolve(); + fetchFieldList().then(function (results) { + if (lastPromise === samplePromise) { + updateFieldList(results); + } }); } }); @@ -218,10 +183,63 @@ define(function (require) { }); } + function fetchFieldList() { + index.dateFields = index.timeField = index.listUsed = null; + var useIndexList = index.isTimeBased && index.nameIsPattern; + var fetchFieldsError; + var dateFields; + + // we don't have enough info to continue + if (!index.name) { + fetchFieldsError = 'Set an index name first'; + return; + } + + if (useIndexList && !index.nameInterval) { + fetchFieldsError = 'Select the interval at which your indices are populated.'; + return; + } + + return indexPatterns.mapper.clearCache(index.name) + .then(function () { + var pattern = mockIndexPattern(index); + + return indexPatterns.mapper.getFieldsForIndexPattern(pattern, true) + .catch(function (err) { + // TODO: we should probably display a message of some kind + if (err instanceof MissingIndices) { + fetchFieldsError = 'Unable to fetch mapping. Do you have indices matching the pattern?'; + return []; + } + + throw err; + }); + }) + .then(function (fields) { + if (fields.length > 0) { + fetchFieldsError = null; + dateFields = fields.filter(function (field) { + return field.type === 'date'; + }); + } + + return { + fetchFieldsError: fetchFieldsError, + dateFields: dateFields + }; + }, notify.fatal); + } + + function updateFieldList(results) { + index.fetchFieldsError = results.fetchFieldsError; + index.dateFields = results.dateFields; + } + function promiseMatch(lastPromise, cb) { if (lastPromise === samplePromise) { cb(); - } else { + } else if (samplePromise != null) { + // haven't hit the last promise yet, reset index params index.patternErrors = []; index.samples = null; index.existing = null;