Refactor loading saved objects (#9406)

* Refactor code that loads saved objects

* couple fixes and comments

cleanup & comments

cleanup

* Use es6 classes, make a note about getting rid of the 100 hard limit on results

* Address comments

* fix linter

* lint fixes with { } spacing

* Fix linter

* fix forgotten merge conflict

* and linter
This commit is contained in:
Stacey Gammon 2016-12-19 10:32:52 -05:00 committed by GitHub
parent eeb4a7dfeb
commit 1924d28ed8
6 changed files with 141 additions and 271 deletions

View file

@ -3,6 +3,7 @@ import Scanner from 'ui/utils/scanner';
import 'plugins/kibana/dashboard/services/_saved_dashboard';
import uiModules from 'ui/modules';
const module = uiModules.get('app/dashboard');
import { SavedObjectLoader } from 'ui/courier/saved_object/saved_object_loader';
// bring in the factory
@ -15,80 +16,6 @@ require('plugins/kibana/management/saved_object_registry').register({
});
// This is the only thing that gets injected into controllers
module.service('savedDashboards', function (Promise, SavedDashboard, kbnIndex, es, kbnUrl) {
const scanner = new Scanner(es, {
index: kbnIndex,
type: 'dashboard'
});
this.type = SavedDashboard.type;
this.Class = SavedDashboard;
this.loaderProperties = {
name: 'dashboards',
noun: 'Dashboard',
nouns: 'dashboards'
};
// Returns a single dashboard by ID, should be the name of the dashboard
this.get = function (id) {
// Returns a promise that contains a dashboard which is a subclass of docSource
return (new SavedDashboard(id)).init();
};
this.urlFor = function (id) {
return kbnUrl.eval('#/dashboard/{{id}}', { id: id });
};
this.delete = function (ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, function (id) {
return (new SavedDashboard(id)).delete();
});
};
this.scanAll = function (queryString, pageSize = 1000) {
return scanner.scanAndMap(queryString, {
pageSize,
docCount: Infinity
}, (hit) => this.mapHits(hit));
};
this.mapHits = function (hit) {
const source = hit._source;
source.id = hit._id;
source.url = this.urlFor(hit._id);
return source;
};
this.find = function (searchString, size = 100) {
let body;
if (searchString) {
body = {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
}
}
};
} else {
body = { query: { match_all: {} } };
}
return es.search({
index: kbnIndex,
type: 'dashboard',
body: body,
size: size
})
.then((resp) => {
return {
total: resp.hits.total,
hits: resp.hits.hits.map((hit) => this.mapHits(hit))
};
});
};
module.service('savedDashboards', function (SavedDashboard, kbnIndex, es, kbnUrl) {
return new SavedObjectLoader(SavedDashboard, kbnIndex, es, kbnUrl);
});

View file

@ -3,7 +3,7 @@ import Scanner from 'ui/utils/scanner';
import 'plugins/kibana/discover/saved_searches/_saved_search';
import 'ui/notify';
import uiModules from 'ui/modules';
import { SavedObjectLoader } from 'ui/courier/saved_object/saved_object_loader';
const module = uiModules.get('discover/saved_searches', [
'kibana/notify'
@ -17,82 +17,16 @@ require('plugins/kibana/management/saved_object_registry').register({
});
module.service('savedSearches', function (Promise, config, kbnIndex, es, createNotifier, SavedSearch, kbnUrl) {
const scanner = new Scanner(es, {
index: kbnIndex,
type: 'search'
});
const notify = createNotifier({
location: 'Saved Searches'
});
this.type = SavedSearch.type;
this.Class = SavedSearch;
this.loaderProperties = {
const savedSearchLoader = new SavedObjectLoader(SavedSearch, kbnIndex, es, kbnUrl);
// Customize loader properties since adding an 's' on type doesn't work for type 'search' .
savedSearchLoader.loaderProperties = {
name: 'searches',
noun: 'Saved Search',
nouns: 'saved searches'
};
this.scanAll = function (queryString, pageSize = 1000) {
return scanner.scanAndMap(queryString, {
pageSize,
docCount: Infinity
}, (hit) => this.mapHits(hit));
};
this.get = function (id) {
return (new SavedSearch(id)).init();
};
this.urlFor = function (id) {
savedSearchLoader.urlFor = function (id) {
return kbnUrl.eval('#/discover/{{id}}', { id: id });
};
this.delete = function (ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, function (id) {
return (new SavedSearch(id)).delete();
});
};
this.mapHits = function (hit) {
const source = hit._source;
source.id = hit._id;
source.url = this.urlFor(hit._id);
return source;
};
this.find = function (searchString, size = 100) {
let body;
if (searchString) {
body = {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
}
}
};
} else {
body = { query: { match_all: {} } };
}
return es.search({
index: kbnIndex,
type: 'search',
body: body,
size: size
})
.then((resp) => {
return {
total: resp.hits.total,
hits: resp.hits.hits.map((hit) => this.mapHits(hit))
};
});
};
return savedSearchLoader;
});

View file

@ -3,6 +3,8 @@ import Scanner from 'ui/utils/scanner';
import 'plugins/kibana/visualize/saved_visualizations/_saved_vis';
import RegistryVisTypesProvider from 'ui/registry/vis_types';
import uiModules from 'ui/modules';
import { SavedObjectLoader } from 'ui/courier/saved_object/saved_object_loader';
const app = uiModules.get('app/visualize');
@ -15,48 +17,12 @@ require('plugins/kibana/management/saved_object_registry').register({
app.service('savedVisualizations', function (Promise, es, kbnIndex, SavedVis, Private, Notifier, kbnUrl) {
const visTypes = Private(RegistryVisTypesProvider);
const scanner = new Scanner(es, {
index: kbnIndex,
type: 'visualization'
});
const notify = new Notifier({
location: 'Saved Visualization Service'
});
this.type = SavedVis.type;
this.Class = SavedVis;
this.loaderProperties = {
name: 'visualizations',
noun: 'Visualization',
nouns: 'visualizations'
};
this.get = function (id) {
return (new SavedVis(id)).init();
};
this.urlFor = function (id) {
return kbnUrl.eval('#/visualize/edit/{{id}}', { id: id });
};
this.delete = function (ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, function (id) {
return (new SavedVis(id)).delete();
});
};
this.scanAll = function (queryString, pageSize = 1000) {
return scanner.scanAndMap(queryString, {
pageSize,
docCount: Infinity
}, (hit) => this.mapHits(hit));
};
this.mapHits = function (hit) {
const saveVisualizationLoader = new SavedObjectLoader(SavedVis, kbnIndex, es, kbnUrl);
saveVisualizationLoader.mapHits = function (hit) {
const source = hit._source;
source.id = hit._id;
source.url = this.urlFor(hit._id);
@ -78,34 +44,9 @@ app.service('savedVisualizations', function (Promise, es, kbnIndex, SavedVis, Pr
return source;
};
this.find = function (searchString, size = 100) {
let body;
if (searchString) {
body = {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND',
analyze_wildcard: true
}
}
};
} else {
body = { query: { match_all: {} } };
}
return es.search({
index: kbnIndex,
type: 'visualization',
body: body,
size: size
})
.then((resp) => {
return {
total: resp.hits.total,
hits: resp.hits.hits.map((hit) => this.mapHits(hit))
};
});
saveVisualizationLoader.urlFor = function (id) {
return kbnUrl.eval('#/visualize/edit/{{id}}', { id: id });
};
return saveVisualizationLoader;
});

View file

@ -1,3 +1,5 @@
import { SavedObjectLoader } from 'ui/courier/saved_object/saved_object_loader';
define(function (require) {
const module = require('ui/modules').get('app/sheet');
const _ = require('lodash');
@ -14,66 +16,17 @@ define(function (require) {
// This is the only thing that gets injected into controllers
module.service('savedSheets', function (Promise, SavedSheet, kbnIndex, es, kbnUrl) {
this.type = SavedSheet.type;
this.Class = SavedSheet;
const savedSheetLoader = new SavedObjectLoader(SavedSheet, kbnIndex, es, kbnUrl);
savedSheetLoader.urlFor = function (id) {
return kbnUrl.eval('#/{{id}}', { id: id });
};
this.loaderProperties = {
// Customize loader properties since adding an 's' on type doesn't work for type 'timelion-sheet'.
savedSheetLoader.loaderProperties = {
name: 'timelion-sheet',
noun: 'Saved Sheets',
nouns: 'saved sheets'
};
// Returns a single sheet by ID, should be the name of the sheet
this.get = function (id) {
// Returns a promise that contains a sheet which is a subclass of docSource
return (new SavedSheet(id)).init();
};
this.urlFor = function (id) {
return kbnUrl.eval('#/{{id}}', { id: id });
};
this.delete = function (ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, function (id) {
return (new SavedSheet(id)).delete();
});
};
this.find = function (searchString) {
const self = this;
let body;
if (searchString) {
body = {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
}
}
};
} else {
body = { query: { match_all: {} } };
}
return es.search({
index: kbnIndex,
type: 'timelion-sheet',
body: body,
size: 1000
})
.then(function (resp) {
return {
total: resp.hits.total,
hits: resp.hits.hits.map(function (hit) {
const source = hit._source;
source.id = hit._id;
source.url = self.urlFor(hit._id);
return source;
})
};
});
};
return savedSheetLoader;
});
});

View file

@ -0,0 +1,104 @@
import _ from 'lodash';
import Scanner from 'ui/utils/scanner';
import { StringUtils } from 'ui/utils/string_utils';
export class SavedObjectLoader {
constructor(SavedObjectClass, kbnIndex, es, kbnUrl) {
this.type = SavedObjectClass.type;
this.Class = SavedObjectClass;
this.lowercaseType = this.type.toLowerCase();
this.kbnIndex = kbnIndex;
this.kbnUrl = kbnUrl;
this.es = es;
this.scanner = new Scanner(es, {
index: kbnIndex,
type: this.lowercaseType
});
this.loaderProperties = {
name: `${ this.lowercaseType }s`,
noun: StringUtils.upperFirst(this.type),
nouns: `${ this.lowercaseType }s`,
};
}
/**
* Retrieve a saved object by id. Returns a promise that completes when the object finishes
* initializing.
* @param id
* @returns {Promise<SavedObject>}
*/
get(id) {
return (new this.Class(id)).init();
}
urlFor(id) {
return this.kbnUrl.eval(`#/${ this.lowercaseType }/{{id}}`, { id: id });
}
delete(ids) {
ids = !_.isArray(ids) ? [ids] : ids;
return Promise.map(ids, (id) => {
return (new this.Class(id)).delete();
});
}
/**
* Updates hit._source to contain an id and url field, and returns the updated
* source object.
* @param hit
* @returns {hit._source} The modified hit._source object, with an id and url field.
*/
mapHits(hit) {
const source = hit._source;
source.id = hit._id;
source.url = this.urlFor(hit._id);
return source;
}
scanAll(queryString, pageSize = 1000) {
return this.scanner.scanAndMap(queryString, {
pageSize,
docCount: Infinity
}, (hit) => this.mapHits(hit));
}
/**
* TODO: Rather than use a hardcoded limit, implement pagination. See
* https://github.com/elastic/kibana/issues/8044 for reference.
*
* @param searchString
* @param size
* @returns {Promise}
*/
find(searchString, size = 100) {
let body;
if (searchString) {
body = {
query: {
simple_query_string: {
query: searchString + '*',
fields: ['title^3', 'description'],
default_operator: 'AND'
}
}
};
} else {
body = { query: { match_all: {} } };
}
return this.es.search({
index: this.kbnIndex,
type: this.type.toLowerCase(),
body,
size
})
.then((resp) => {
return {
total: resp.hits.total,
hits: resp.hits.hits.map((hit) => this.mapHits(hit))
};
});
}
}

View file

@ -0,0 +1,11 @@
export class StringUtils {
/**
* Returns a version of the string with the first letter capitalized.
* @param str {string}
* @returns {string}
*/
static upperFirst(str) {
return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
}
}