UNPKG

@spalger/kibana

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

298 lines (248 loc) 8.93 kB
define(function (require) { return function SavedObjectFactory(es, kbnIndex, Promise, Private, Notifier, indexPatterns) { var angular = require('angular'); var errors = require('ui/errors'); var _ = require('lodash'); var slugifyId = require('ui/utils/slugify_id'); var DocSource = Private(require('ui/courier/data_source/doc_source')); var SearchSource = Private(require('ui/courier/data_source/search_source')); var mappingSetup = Private(require('ui/utils/mapping_setup')); function SavedObject(config) { if (!_.isObject(config)) config = {}; // save an easy reference to this var self = this; /************ * Initialize config vars ************/ // the doc which is used to store this object var docSource = new DocSource(); // type name for this object, used as the ES-type var type = config.type; // Create a notifier for sending alerts var notify = new Notifier({ location: 'Saved ' + type }); // mapping definition for the fields that this object will expose var mapping = mappingSetup.expandShorthand(config.mapping); // default field values, assigned when the source is loaded var defaults = config.defaults || {}; var afterESResp = config.afterESResp || _.noop; var customInit = config.init || _.noop; // optional search source which this object configures self.searchSource = config.searchSource && new SearchSource(); // the id of the document self.id = config.id || void 0; self.defaults = config.defaults; /** * Asynchronously initialize this object - will only run * once even if called multiple times. * * @return {Promise} * @resolved {SavedObject} */ self.init = _.once(function () { // ensure that the type is defined if (!type) throw new Error('You must define a type name to use SavedObject objects.'); // tell the docSource where to find the doc docSource .index(kbnIndex) .type(type) .id(self.id); // check that the mapping for this type is defined return mappingSetup.isDefined(type) .then(function (defined) { // if it is already defined skip this step if (defined) return true; mapping.kibanaSavedObjectMeta = { properties: { // setup the searchSource mapping, even if it is not used but this type yet searchSourceJSON: { type: 'string' } } }; // tell mappingSetup to set type return mappingSetup.setup(type, mapping); }) .then(function () { // If there is not id, then there is no document to fetch from elasticsearch if (!self.id) { // just assign the defaults and be done _.assign(self, defaults); return hydrateIndexPattern().then(function () { return afterESResp.call(self); }); } // fetch the object from ES return docSource.fetch() .then(self.applyESResp); }) .then(function () { return customInit.call(self); }) .then(function () { // return our obj as the result of init() return self; }); }); self.applyESResp = function (resp) { self._source = _.cloneDeep(resp._source); if (resp.found != null && !resp.found) throw new errors.SavedObjectNotFound(type, self.id); var meta = resp._source.kibanaSavedObjectMeta || {}; delete resp._source.kibanaSavedObjectMeta; if (!config.indexPattern && self._source.indexPattern) { config.indexPattern = self._source.indexPattern; delete self._source.indexPattern; } // assign the defaults to the response _.defaults(self._source, defaults); // transform the source using _deserializers _.forOwn(mapping, function ittr(fieldMapping, fieldName) { if (fieldMapping._deserialize) { self._source[fieldName] = fieldMapping._deserialize(self._source[fieldName], resp, fieldName, fieldMapping); } }); // Give obj all of the values in _source.fields _.assign(self, self._source); return Promise.try(function () { parseSearchSource(meta.searchSourceJSON); }) .then(hydrateIndexPattern) .then(function () { return Promise.cast(afterESResp.call(self, resp)); }) .then(function () { // Any time obj is updated, re-call applyESResp docSource.onUpdate().then(self.applyESResp, notify.fatal); }); }; function parseSearchSource(searchSourceJson) { if (!self.searchSource) return; // if we have a searchSource, set its state based on the searchSourceJSON field var state; try { state = JSON.parse(searchSourceJson); } catch (e) { state = {}; } var oldState = self.searchSource.toJSON(); var fnProps = _.transform(oldState, function (dynamic, val, name) { if (_.isFunction(val)) dynamic[name] = val; }, {}); self.searchSource.set(_.defaults(state, fnProps)); } /** * After creation or fetching from ES, ensure that the searchSources index indexPattern * is an bonafide IndexPattern object. * * @return {[type]} [description] */ function hydrateIndexPattern() { return Promise.try(function () { if (self.searchSource) { var index = config.indexPattern || self.searchSource.getOwn('index'); if (!index) return; if (config.clearSavedIndexPattern) { self.searchSource.set('index', undefined); return; } if (!(index instanceof indexPatterns.IndexPattern)) { index = indexPatterns.get(index); } return Promise.resolve(index).then(function (indexPattern) { self.searchSource.set('index', indexPattern); }); } }); } /** * Serialize this object * * @return {Object} */ self.serialize = function () { var body = {}; _.forOwn(mapping, function (fieldMapping, fieldName) { if (self[fieldName] != null) { body[fieldName] = (fieldMapping._serialize) ? fieldMapping._serialize(self[fieldName]) : self[fieldName]; } }); if (self.searchSource) { body.kibanaSavedObjectMeta = { searchSourceJSON: angular.toJson(_.omit(self.searchSource.toJSON(), ['sort', 'size'])) }; } return body; }; /** * Save this object * * @return {Promise} * @resolved {String} - The id of the doc */ self.save = function () { var body = self.serialize(); // Slugify the object id self.id = slugifyId(self.id); // ensure that the docSource has the current self.id docSource.id(self.id); // index the document return self.saveSource(body); }; self.saveSource = function (source) { var finish = function (id) { self.id = id; return es.indices.refresh({ index: kbnIndex }) .then(function () { return self.id; }); }; return docSource.doCreate(source) .then(finish) .catch(function (err) { // record exists, confirm overwriting if (_.get(err, 'origError.status') === 409) { var confirmMessage = 'Are you sure you want to overwrite ' + self.title + '?'; if (window.confirm(confirmMessage)) { // eslint-disable-line no-alert return docSource.doIndex(source).then(finish); } // if the user doesn't overwrite record, just swallow the error return; } return Promise.reject(err); }); }; /** * Destroy this object * * @return {undefined} */ self.destroy = function () { docSource.cancelQueued(); if (self.searchSource) { self.searchSource.cancelQueued(); } }; /** * Delete this object from Elasticsearch * @return {promise} */ self.delete = function () { return es.delete({ index: kbnIndex, type: type, id: this.id }).then(function () { return es.indices.refresh({ index: kbnIndex }); }); }; } return SavedObject; }; });