Skip to content

Commit

Permalink
Save/rename flow fix (#9333)
Browse files Browse the repository at this point in the history
Backports PR #9087

**Commit 1:**
Save/rename flow fix

- Use auto generated ids instead of coupling the id to the title which
creates problems.
- Adjust UI to make the save flow more understandable.
- Remove confirmation on overwrite since we will now be creating
duplicate objects even if they have the same title.

* Original sha: faf3e9c
* Authored by Stacey Gammon <[email protected]> on 2016-11-15T20:45:16Z

**Commit 2:**
use undefined instead of null

* Original sha: 08b0e92
* Authored by Stacey Gammon <[email protected]> on 2016-11-21T17:35:56Z

**Commit 3:**
Change titleChanged function name

* Original sha: b7b6e0a
* Authored by Stacey Gammon <[email protected]> on 2016-11-21T17:40:35Z

**Commit 4:**
Merge branch 'master' of https://github.com/elastic/kibana into saved_object_refactor

* Original sha: 07901bd
* Authored by Stacey Gammon <[email protected]> on 2016-11-21T20:58:09Z

**Commit 5:**
address code review comments

* Original sha: 41441bf
* Authored by Stacey Gammon <[email protected]> on 2016-11-22T20:10:09Z

**Commit 6:**
Merge branch 'master' of https://github.com/elastic/kibana into saved_object_refactor

* Original sha: e3ff0ad
* Authored by Stacey Gammon <[email protected]> on 2016-11-28T14:43:23Z

**Commit 7:**
Add isSaving flag to avoid checkbox flicker, fix regression bug from refactor.
Added tests and fixed a couple bugs
Updated info message

* Original sha: cf04dde
* Authored by Stacey Gammon <[email protected]> on 2016-11-28T14:45:52Z

**Commit 8:**
Update doc and nav title with new name on rename
don't hardcode Dashboard

* Original sha: 9686b2a
* Authored by Stacey Gammon <[email protected]> on 2016-11-30T21:08:59Z

**Commit 9:**
Merge branch 'master' of https://github.com/elastic/kibana into saved_object_refactor

* Original sha: cb6f918
* Authored by Stacey Gammon <[email protected]> on 2016-12-02T14:23:30Z
  • Loading branch information
elastic-jasper authored and stacey-gammon committed Dec 2, 2016
1 parent 37a6294 commit 93f1282
Show file tree
Hide file tree
Showing 20 changed files with 269 additions and 169 deletions.
2 changes: 1 addition & 1 deletion src/core_plugins/kibana/public/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
>
<span
ng-show="dash.id"
ng-bind="::dash.title"
ng-bind="dash.lastSavedTitle"
></span>
</div>

Expand Down
6 changes: 4 additions & 2 deletions src/core_plugins/kibana/public/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,11 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,

courier.setRootSearchSource(dash.searchSource);

const docTitle = Private(DocTitleProvider);

function init() {
updateQueryOnRootSource();

const docTitle = Private(DocTitleProvider);
if (dash.id) {
docTitle.change(dash.title);
}
Expand Down Expand Up @@ -227,7 +228,6 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
};

$scope.save = function () {
$state.title = dash.id = dash.title;
$state.save();

const timeRestoreObj = _.pick(timefilter.refreshInterval, ['display', 'pause', 'section', 'value']);
Expand All @@ -246,6 +246,8 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter,
notify.info('Saved Dashboard as "' + dash.title + '"');
if (dash.id !== $routeParams.id) {
kbnUrl.change('/dashboard/{{id}}', {id: dash.id});
} else {
docTitle.change(dash.lastSavedTitle);
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
role="form"
ng-submit="opts.save()"
>
<div class="localDropdownTitle">Save Dashboard</div>
<div class="localDropdownTitle">Save {{opts.dashboard.getDisplayName()}}</div>
<input
class="localDropdownInput"
id="dashboardTitle"
Expand All @@ -11,12 +11,18 @@
placeholder="Dashboard title"
input-focus="select"
>

<saved-object-save-as-check-box saved-object="opts.dashboard"></saved-object-save-as-check-box>

<div class="form-group">
<label>
<input type="checkbox" ng-model="opts.dashboard.timeRestore" ng-checked="opts.dashboard.timeRestore">
Store time with dashboard
<kbn-info info="Change the time filter to the currently selected time each time this dashboard is loaded"></kbn-info>
Store time with {{opts.dashboard.getDisplayName()}}
<kbn-info placement="bottom" info="Change the time filter to the currently selected time each time this dashboard is loaded"></kbn-info>
</label>
</div>
<button type="submit" ng-disabled="!opts.dashboard.title" class="btn btn-primary" aria-label="Save dashboard">Save</button>

<button type="submit" ng-disabled="!opts.dashboard.title" class="btn btn-primary" aria-label="Save dashboard">
Save
</button>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,6 @@ function discoverController($scope, config, courier, $route, $window, Notifier,
$scope.opts.saveDataSource = function () {
return $scope.updateDataSource()
.then(function () {
savedSearch.id = savedSearch.title;
savedSearch.columns = $scope.state.columns;
savedSearch.sort = $scope.state.sort;

Expand All @@ -334,6 +333,7 @@ function discoverController($scope, config, courier, $route, $window, Notifier,
} else {
// Update defaults so that "reload saved query" functions correctly
$state.setDefaults(getStateDefaults());
docTitle.change(savedSearch.lastSavedTitle);
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/kibana/public/discover/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div data-transclude-slot="topLeftCorner" class="localBreadcrumbs">
<div class="localBreadcrumb">
<span ng-show="opts.savedSearch.id" class="localBreadcrumb__emphasis">
<span data-test-subj="discoverCurrentQuery" ng-bind="::opts.savedSearch.title"></span>
<span data-test-subj="discoverCurrentQuery" ng-bind="opts.savedSearch.lastSavedTitle"></span>
<i aria-label="Reload Saved Search" tooltip="Reload Saved Search" ng-click="resetQuery();" class="fa fa-undo small"></i>&nbsp;
</span>
<span data-test-subj="discoverQueryHits" class="localBreadcrumb__emphasis">{{(hits || 0) | number:0}}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
input-focus="select"
placeholder="Name this search..."
>

<saved-object-save-as-check-box saved-object="opts.savedSearch"></saved-object-save-as-check-box>
<button ng-disabled="!opts.savedSearch.title" data-test-subj="discover-save-search-btn" type="submit" class="btn btn-primary">
Save
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
>
<span
ng-show="savedVis.id"
ng-bind="::savedVis.title"
ng-bind="savedVis.lastSavedTitle"
></span>
</div>

Expand Down
8 changes: 5 additions & 3 deletions src/core_plugins/kibana/public/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ function VisEditor($scope, $route, timefilter, AppState, $location, kbnUrl, $tim
* Called when the user clicks "Save" button.
*/
$scope.doSave = function () {
savedVis.id = savedVis.title;
// vis.title was not bound and it's needed to reflect title into visState
$state.vis.title = savedVis.title;
savedVis.visState = $state.vis;
Expand All @@ -305,8 +304,11 @@ function VisEditor($scope, $route, timefilter, AppState, $location, kbnUrl, $tim

if (id) {
notify.info('Saved Visualization "' + savedVis.title + '"');
if (savedVis.id === $route.current.params.id) return;
kbnUrl.change('/visualize/edit/{{id}}', {id: savedVis.id});
if (savedVis.id === $route.current.params.id) {
docTitle.change(savedVis.lastSavedTitle);
} else {
kbnUrl.change('/visualize/edit/{{id}}', {id: savedVis.id});
}
}
}, notify.fatal);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
ng-model="opts.savedVis.title"
required
>

<saved-object-save-as-check-box saved-object="opts.savedVis"></saved-object-save-as-check-box>

<button
data-test-subj="saveVisualizationButton"
type="submit"
Expand Down
4 changes: 1 addition & 3 deletions src/core_plugins/timelion/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ app.controller('timelion', function (
}
});

$scope.$watch(function () { return savedSheet.title; }, function (newTitle) {
$scope.$watch(function () { return savedSheet.lastSavedTitle; }, function (newTitle) {
docTitle.change(savedSheet.id ? newTitle : undefined);
});

Expand Down Expand Up @@ -222,7 +222,6 @@ app.controller('timelion', function (
$scope.safeSearch = _.debounce($scope.search, 500);

function saveSheet() {
savedSheet.id = savedSheet.title;
savedSheet.timelion_sheet = $scope.state.sheet;
savedSheet.timelion_interval = $scope.state.interval;
savedSheet.timelion_columns = $scope.state.columns;
Expand All @@ -239,7 +238,6 @@ app.controller('timelion', function (

function saveExpression(title) {
savedVisualizations.get({type: 'timelion'}).then(function (savedExpression) {
savedExpression.id = title;
savedExpression.visState.params = {
expression: $scope.state.sheet[$scope.state.selected],
interval: $scope.state.interval
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/timelion/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<div data-transclude-slots>
<div data-transclude-slot="topLeftCorner">
<span class="localTitle" ng-show="opts.savedSheet.id">
{{opts.savedSheet.title}}
{{opts.savedSheet.lastSavedTitle}}
&nbsp;
<span class="fa fa-bolt" ng-click="showStats = !showStats"></span>
&nbsp;
Expand Down
6 changes: 3 additions & 3 deletions src/core_plugins/timelion/public/partials/save_sheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ <h4 class="list-group-item-heading">Save entire Timelion sheet</h4>
<label for="savedSheet" class="control-label">Save sheet as</label>
<input id="savedSheet" ng-model="opts.savedSheet.title" input-focus="select" class="form-control" placeholder="Name this sheet...">
</div>

<saved-object-save-as-check-box saved-object="opts.savedSheet"></saved-object-save-as-check-box>
<div class="form-group">
<button ng-disabled="!opts.savedSheet.title" type="submit" class="btn btn-primary">
Save
Expand Down Expand Up @@ -43,9 +45,7 @@ <h4 class="list-group-item-heading">Save current expression as Kibana dashboard
<input id="savedExpression" ng-model="panelTitle" input-focus="select" class="form-control" placeholder="Name this panel">
</div>
<div class="form-group">
<button ng-disabled="!panelTitle" type="submit" class="btn btn-primary">
Save
</button>
<button ng-disabled="!panelTitle" type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/ui/public/autoload/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ import 'ui/typeahead';
import 'ui/url';
import 'ui/validate_date_interval';
import 'ui/watch_multi';
import 'ui/courier/saved_object/ui/saved_object_save_as_checkbox';
110 changes: 108 additions & 2 deletions src/ui/public/courier/__tests__/saved_object.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
import ngMock from 'ng_mock';
import expect from 'expect.js';
import sinon from 'auto-release-sinon';

import BluebirdPromise from 'bluebird';

import SavedObjectFactory from '../saved_object/saved_object';
import { stubMapper } from 'test_utils/stub_mapper';
import IndexPatternFactory from 'ui/index_patterns/_index_pattern';
import DocSourceProvider from '../data_source/doc_source';

import { stubMapper } from 'test_utils/stub_mapper';


describe('Saved Object', function () {
require('test_utils/no_digest_promises').activateForSuite();

let SavedObject;
let IndexPattern;
let esStub;
let DocSource;

/**
* Some default es stubbing to avoid timeouts and allow a default type of 'dashboard'.
Expand All @@ -35,6 +39,7 @@ describe('Saved Object', function () {

// Necessary to avoid a timeout condition.
sinon.stub(esStub.indices, 'putMapping').returns(BluebirdPromise.resolve());
sinon.stub(esStub.indices, 'refresh').returns(BluebirdPromise.resolve());
}

/**
Expand Down Expand Up @@ -83,11 +88,112 @@ describe('Saved Object', function () {
SavedObject = Private(SavedObjectFactory);
IndexPattern = Private(IndexPatternFactory);
esStub = es;
DocSource = Private(DocSourceProvider);

mockEsService();
stubMapper(Private);
}));

describe('save', function () {
describe(' with copyOnSave', function () {
it('as true creates a copy on save success', function () {
const mockDocResponse = getMockedDocResponse('myId');
stubESResponse(mockDocResponse);
let newUniqueId;
return createInitializedSavedObject({type: 'dashboard', id: 'myId'}).then(savedObject => {
sinon.stub(DocSource.prototype, 'doIndex', function () {
newUniqueId = savedObject.id;
expect(newUniqueId).to.not.be('myId');
mockDocResponse._id = newUniqueId;
return BluebirdPromise.resolve(newUniqueId);
});
savedObject.copyOnSave = true;
return savedObject.save()
.then((id) => {
expect(id).to.be(newUniqueId);
});
});
});

it('as true does not create a copy when save fails', function () {
const mockDocResponse = getMockedDocResponse('myId');
stubESResponse(mockDocResponse);
let originalId = 'id1';
return createInitializedSavedObject({type: 'dashboard', id: originalId}).then(savedObject => {
sinon.stub(DocSource.prototype, 'doIndex', function () {
return BluebirdPromise.reject('simulated error');
});
savedObject.copyOnSave = true;
return savedObject.save().then(() => {
throw new Error('Expected a rejection');
}).catch(() => {
expect(savedObject.id).to.be(originalId);
});
});
});

it('as false does not create a copy', function () {
const mockDocResponse = getMockedDocResponse('myId');
stubESResponse(mockDocResponse);
const id = 'myId';
return createInitializedSavedObject({type: 'dashboard', id: id}).then(savedObject => {
sinon.stub(DocSource.prototype, 'doIndex', function () {
expect(savedObject.id).to.be(id);
return BluebirdPromise.resolve(id);
});
savedObject.copyOnSave = false;
return savedObject.save()
.then((id) => {
expect(id).to.be(id);
});
});
});
});

it('returns id from server on success', function () {
return createInitializedSavedObject({type: 'dashboard'}).then(savedObject => {
const mockDocResponse = getMockedDocResponse('myId');
stubESResponse(mockDocResponse);
return savedObject.save()
.then((id) => {
expect(id).to.be('myId');
});
});
});

describe('updates isSaving variable', function () {
it('on success', function () {
let id = 'id';
stubESResponse(getMockedDocResponse(id));
return createInitializedSavedObject({type: 'dashboard', id: id}).then(savedObject => {
sinon.stub(DocSource.prototype, 'doIndex', () => {
expect(savedObject.isSaving).to.be(true);
return BluebirdPromise.resolve(id);
});
expect(savedObject.isSaving).to.be(false);
return savedObject.save()
.then((id) => {
expect(savedObject.isSaving).to.be(false);
});
});
});

it('on failure', function () {
return createInitializedSavedObject({type: 'dashboard'}).then(savedObject => {
sinon.stub(DocSource.prototype, 'doIndex', () => {
expect(savedObject.isSaving).to.be(true);
return BluebirdPromise.reject();
});
expect(savedObject.isSaving).to.be(false);
return savedObject.save()
.catch(() => {
expect(savedObject.isSaving).to.be(false);
});
});
});
});
});

describe('applyESResp', function () {
it('throws error if not found', function () {
return createInitializedSavedObject({ type: 'dashboard' }).then(savedObject => {
Expand Down
Loading

0 comments on commit 93f1282

Please sign in to comment.