UNPKG

kibana-with-account

Version:

kibana-with-account is develop based kibana project

298 lines (249 loc) 8.95 kB
define(function (require) { return function SavedObjectFactory(es, kbnIndex, Promise, Private, Notifier, safeConfirm, 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 + '?'; return safeConfirm(confirmMessage).then( function () { return docSource.doIndex(source).then(finish); }, _.noop // if the user doesn't overwrite record, just swallow the error ); } 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; }; });