Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix saved objects #1627

Merged
merged 11 commits into from
Oct 10, 2014
1 change: 0 additions & 1 deletion src/kibana/apps/dashboard/directives/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ define(function (require) {
})
.catch(function (e) {
$scope.error = e.message;
console.log(e);
});
});

Expand Down
4 changes: 3 additions & 1 deletion src/kibana/apps/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ define(function (require) {
resolve: {
dash: function (savedDashboards, Notifier, $route, $location, courier) {
return savedDashboards.get($route.current.params.id)
.catch(courier.redirectWhenMissing('/dashboard'));
.catch(courier.redirectWhenMissing({
'dashboard' : '/dashboard'
}));
}
}
});
Expand Down
11 changes: 6 additions & 5 deletions src/kibana/apps/dashboard/services/_saved_dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ define(function (require) {

// SavedDashboard constructor. Usually you'd interact with an instance of this.
// ID is option, without it one will be generated on save.
_(SavedDashboard).inherits(courier.SavedObject);
function SavedDashboard(id) {

// Gives our SavedDashboard the properties of a SavedObject
courier.SavedObject.call(this, {
// this object will be saved at {{configFile.kibanaIndex}}/dashboard/{{id}}
type: 'dashboard',
type: SavedDashboard.type,

// if this is null/undefined then the SavedObject will be assigned the defaults
id: id,
Expand All @@ -34,12 +34,13 @@ define(function (require) {
panelsJSON: '[]'
},

searchSource: true
searchSource: true,

clearSavedIndexPattern: true
});
}

// Sets savedDashboard.prototype to an instance of SavedObject
inherits(SavedDashboard, courier.SavedObject);
SavedDashboard.type = 'dashboard';

return SavedDashboard;
});
Expand Down
1 change: 1 addition & 0 deletions src/kibana/apps/dashboard/services/saved_dashboards.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ define(function (require) {

// This is the only thing that gets injected into controllers
module.service('savedDashboards', function (Promise, SavedDashboard, config, es, kbnUrl) {
this.type = SavedDashboard.type;

// Returns a single dashboard by ID, should be the name of the dashboard
this.get = function (id) {
Expand Down
4 changes: 2 additions & 2 deletions src/kibana/apps/discover/controllers/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ define(function (require) {
savedSearch: function (courier, savedSearches, $route) {
return savedSearches.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'index-pattern': '/settings/indices',
'*': '/discover'
'search': '/discover',
'index-pattern': '/settings/objects/savedSearches/' + $route.current.params.id
}));
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/kibana/apps/discover/saved_searches/_saved_search.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ define(function (require) {
]);

module.factory('SavedSearch', function (courier, indexPatterns) {
_(SavedSearch).inherits(courier.SavedObject);
function SavedSearch(id) {
courier.SavedObject.call(this, {
type: 'search',
type: SavedSearch.type,

id: id,

Expand All @@ -31,7 +32,9 @@ define(function (require) {
searchSource: true
});
}
inherits(SavedSearch, courier.SavedObject);

SavedSearch.type = 'search';

return SavedSearch;
});
});
2 changes: 2 additions & 0 deletions src/kibana/apps/discover/saved_searches/saved_searches.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ define(function (require) {
location: 'Saved Searches'
});

this.type = SavedSearch.type;

this.get = function (id) {
return (new SavedSearch(id)).init();
};
Expand Down
2 changes: 1 addition & 1 deletion src/kibana/apps/settings/sections/objects/_objects.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ <h2>Edit Saved Objects</h2>
</div>

<div class="item-title">
<a ng-click="open(item)">{{ item.title }}</a>
<a ng-click="edit(service, item)">{{ item.title }}</a>
</div>
</li>
<li ng-if="!service.data.length" class="empty">No "{{service.title}}" found.</li>
Expand Down
1 change: 0 additions & 1 deletion src/kibana/apps/settings/sections/objects/_objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ define(function (require) {
};

$scope.edit = function (service, item) {
console.log(service, item);
var params = {
service: service.service,
id: item.id
Expand Down
12 changes: 10 additions & 2 deletions src/kibana/apps/settings/sections/objects/_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
<a confirm-click="delete()" class="btn btn-danger"><i class="fa fa-trash-o"></i> Delete {{ title }} Object</a>
</div>
<h1>Edit {{ title }} Object</h1>
<div class="bs-callout bs-callout-danger" ng-if="notFound">
<h4>There is a problem with that saved object</h4>

<p ng-if="notFound === 'search'">The saved search associated with this object no longer exists.</p>
<p ng-if="notFound === 'index-pattern'">The index pattern associated with this object no longer exists.</p>

<p>If you know what this error means, go ahead and fix it - otherwise click the delete button above.</p>
</div>
<div class="bs-callout bs-callout-warning">
<h4>Proceed with caution</h4>
Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn't be.
</div>

<p>Modifying objects is for advanced users only. Object properties are not validated and invalid objects could cause errors, data loss, or worse. Unless someone with intimate knowledge of the code told you to be in here, you probably shouldn't be.</p>
</div>
<form name="objectForm" ng-submit="submit()">
<div class="form-group" ng-repeat="field in fields">
Expand Down
68 changes: 49 additions & 19 deletions src/kibana/apps/settings/sections/objects/_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ define(function (require) {
.directive('kbnSettingsObjectsView', function (config, Notifier) {
return {
restrict: 'E',
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope) {
controller: function ($scope, $injector, $routeParams, $location, $window, $rootScope, es) {
var notify = new Notifier({ location: 'SavedObject view' });

var serviceObj = registry.get($routeParams.service);
var service = $injector.get(serviceObj.service);
Expand Down Expand Up @@ -68,21 +69,30 @@ define(function (require) {
return memo;
};

$scope.notFound = $routeParams.notFound;

$scope.title = inflection.singularize(serviceObj.title);

service.get($routeParams.id).then(function (obj) {
es.get({
index: config.file.kibanaIndex,
type: service.type,
id: $routeParams.id
})
.then(function (obj) {
$scope.obj = obj;
$scope.link = service.urlFor(obj.id);
$scope.link = service.urlFor(obj._id);
$scope.fields = _.reduce(obj._source, createField, []);
});
})
.catch(notify.fatal);

// This handles the validation of the Ace Editor. Since we don't have any
// other hooks into the editors to tell us if the content is valid or not
// we need to use the annotations to see if they have any errors. If they
// do then we push the field.name to aceInvalidEditor variable.
// Otherwise we remove it.
$scope.aceInvalidEditors = [];
var loadedEditors = [];
$scope.aceInvalidEditors = [];

$scope.aceLoaded = function (editor) {
if (_.contains(loadedEditors, editor)) return;
loadedEditors.push(editor);
Expand Down Expand Up @@ -116,13 +126,15 @@ define(function (require) {
* @returns {type} description
*/
$scope.delete = function () {
$scope.obj.delete().then(function (resp) {
$location.path('/settings/objects').search({ _a: rison.encode({
tab: serviceObj.title
})});
var notify = new Notifier();
notify.info('You successfully deleted the "' + $scope.obj.title + '" ' + $scope.title.toLowerCase() + ' object');
});
es.delete({
index: config.file.kibanaIndex,
type: service.type,
id: $routeParams.id
})
.then(function (resp) {
return redirectHandler('deleted');
})
.catch(notify.fatal);
};

$scope.submit = function () {
Expand All @@ -143,15 +155,33 @@ define(function (require) {
_.setValue(source, field.name, field.value);
});

$scope.obj.saveSource(source).then(function (resp) {
$location.path('/settings/objects').search({ _a: rison.encode({
tab: serviceObj.title
})});
var notify = new Notifier();
notify.info('You successfully updated the "' + $scope.obj.title + '" ' + $scope.title.toLowerCase() + ' object');
});
es.index({
index: config.file.kibanaIndex,
type: service.type,
id: $routeParams.id,
body: source
})
.then(function (resp) {
return redirectHandler('updated');
})
.catch(notify.fatal);
};

function redirectHandler(action) {
return es.indices.refresh({
index: config.file.kibanaIndex
})
.then(function (resp) {
var msg = 'You successfully ' + action + ' the "' + $scope.obj._source.title + '" ' + $scope.title.toLowerCase() + ' object';

$location.path('/settings/objects').search({
_a: rison.encode({
tab: serviceObj.title
})
});
notify.info(msg);
});
}
}
};
});
Expand Down
6 changes: 3 additions & 3 deletions src/kibana/apps/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ define(function (require) {

return savedVisualizations.get($route.current.params)
.catch(courier.redirectWhenMissing({
//'index-pattern': '/visualize',
'*': '/visualize'
}));
}
Expand All @@ -30,8 +29,9 @@ define(function (require) {
savedVis: function (savedVisualizations, courier, $route) {
return savedVisualizations.get($route.current.params.id)
.catch(courier.redirectWhenMissing({
'index-pattern': '/settings',
'*': '/visualize'
'visualization': '/visualize',
'search': '/settings/objects/savedVisualizations/' + $route.current.params.id,
'index-pattern': '/settings/objects/savedVisualizations/' + $route.current.params.id
}));
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/kibana/apps/visualize/saved_visualizations/_saved_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ define(function (require) {
if (typeof opts !== 'object') opts = { id: opts };

SavedVis.Super.call(self, {
type: 'visualization',
type: SavedVis.type,

id: opts.id,

Expand Down Expand Up @@ -46,6 +46,8 @@ define(function (require) {
});
}

SavedVis.type = 'visualization';

SavedVis.prototype._afterEsResp = function () {
var self = this;
var linkedSearch = self.savedSearchId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ define(function (require) {
location: 'saved visualization service'
});

this.type = SavedVis.type;

this.get = function (id) {
return (new SavedVis(id)).init();
};
Expand Down
3 changes: 3 additions & 0 deletions src/kibana/components/courier/_redirect_when_missing.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
define(function (require) {
var errors = require('errors');
var qs = require('utils/query_string');

return function RedirectWhenMissingFn($location, kbnUrl, Notifier) {
var SavedObjectNotFound = errors.SavedObjectNotFound;
Expand All @@ -26,6 +27,8 @@ define(function (require) {
var url = mapping[err.savedObjectType] || mapping['*'];
if (!url) url = '/';

url = qs.replaceParamInUrl(url, 'notFound', err.savedObjectType);

notify.error(err);
kbnUrl.change(url);
return;
Expand Down
5 changes: 5 additions & 0 deletions src/kibana/components/courier/data_source/_abstract.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ define(function (require) {
*/
SourceAbstract.prototype.set = function (state, val) {
if (typeof state === 'string') {
// the getter and setter methods check for undefined explicitly
// to identify getters and null to identify deletion
if (val === undefined) {
val = null;
}
this[state](val);
} else {
this._state = state;
Expand Down
3 changes: 2 additions & 1 deletion src/kibana/components/courier/data_source/search_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ define(function (require) {
];

SearchSource.prototype.index = function (indexPattern) {
if (indexPattern == null) return this._state.index;
if (indexPattern === undefined) return this._state.index;
if (indexPattern === null) return delete this._state.index;
if (!indexPattern || typeof indexPattern.toIndexList !== 'function') {
throw new TypeError('expected indexPattern to be an IndexPattern duck.');
}
Expand Down
16 changes: 12 additions & 4 deletions src/kibana/components/courier/saved_object/saved_object.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ define(function (require) {

// tell the docSource where to find the doc
docSource
.index(configFile.kibanaIndex)
.type(type)
.id(obj.id);
.index(configFile.kibanaIndex)
.type(type)
.id(obj.id);

// check that the mapping for this type is defined
return mappingSetup.isDefined(type)
Expand Down Expand Up @@ -94,7 +94,7 @@ define(function (require) {

obj._source = _.cloneDeep(resp._source);

if (!resp.found) throw new errors.SavedObjectNotFound(type);
if (!resp.found) throw new errors.SavedObjectNotFound(type, obj.id);

var meta = resp._source.kibanaSavedObjectMeta || {};
delete resp._source.kibanaSavedObjectMeta;
Expand Down Expand Up @@ -155,9 +155,17 @@ define(function (require) {
function hydrateIndexPattern() {
return Promise.try(function () {
if (obj.searchSource) {


var index = obj.searchSource.get('index') || config.indexPattern;

if (!index) return;

if (config.clearSavedIndexPattern) {
obj.searchSource.set('index', undefined);
return;
}

if (index instanceof indexPatterns.IndexPattern) {
return;
}
Expand Down
6 changes: 4 additions & 2 deletions src/kibana/components/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,12 @@ define(function (require) {
* A saved object was not found
* @param {String} field - the fields which contains the conflict
*/
errors.SavedObjectNotFound = function SavedObjectNotFound(type) {
errors.SavedObjectNotFound = function SavedObjectNotFound(type, id) {
this.savedObjectType = type;
this.savedObjectId = id;
var idMsg = id ? ' (id: ' + id + ')' : '';
KbnError.call(this,
'Could not locate that ' + type,
'Could not locate that ' + type + idMsg,
errors.SavedObjectNotFound);
};
inherits(errors.SavedObjectNotFound, KbnError);
Expand Down
2 changes: 1 addition & 1 deletion src/kibana/components/index_patterns/_index_pattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ define(function (require) {
// fetch the object from ES
return docSource.fetch()
.then(function applyESResp(resp) {
if (!resp.found) throw new errors.SavedObjectNotFound(type);
if (!resp.found) throw new errors.SavedObjectNotFound(type, pattern.id);

// deserialize any json fields
_.forOwn(mapping, function ittr(fieldMapping, name) {
Expand Down