mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
add option to confirm overwrite on save (#9659)
* add option to confirm overwrite on save * Make save options instead of using a boolean to increase readability. * clean up comment * Address code comments - implicit returns from arrow function - throw an error not an object clean up implicit return confusion use promise.reject instead of throw.
This commit is contained in:
parent
024c81697e
commit
e75e2a2100
3 changed files with 141 additions and 5 deletions
|
@ -158,7 +158,7 @@ uiModules.get('apps/management')
|
|||
return service.get().then(function (obj) {
|
||||
obj.id = doc._id;
|
||||
return obj.applyESResp(doc).then(function () {
|
||||
return obj.save();
|
||||
return obj.save({ confirmOverwrite : true });
|
||||
});
|
||||
});
|
||||
})
|
||||
|
|
|
@ -22,6 +22,7 @@ describe('Saved Object', function () {
|
|||
let esAdminStub;
|
||||
let esDataStub;
|
||||
let DocSource;
|
||||
let window;
|
||||
|
||||
/**
|
||||
* Some default es stubbing to avoid timeouts and allow a default type of 'dashboard'.
|
||||
|
@ -86,19 +87,112 @@ describe('Saved Object', function () {
|
|||
return savedObject.init();
|
||||
}
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (es, esAdmin, Private) {
|
||||
beforeEach(ngMock.module('kibana',
|
||||
|
||||
// The default implementation of safeConfirm uses $timeout which will cause
|
||||
// the test environment to hang.
|
||||
function ($provide) {
|
||||
const overrideSafeConfirm = message => window.confirm(message) ? Promise.resolve() : Promise.reject();
|
||||
$provide.decorator('safeConfirm', () => overrideSafeConfirm);
|
||||
})
|
||||
);
|
||||
beforeEach(ngMock.inject(function (es, esAdmin, Private, $window) {
|
||||
SavedObject = Private(SavedObjectFactory);
|
||||
IndexPattern = Private(IndexPatternFactory);
|
||||
esAdminStub = esAdmin;
|
||||
esDataStub = es;
|
||||
DocSource = Private(DocSourceProvider);
|
||||
window = $window;
|
||||
|
||||
mockEsService();
|
||||
stubMapper(Private);
|
||||
}));
|
||||
|
||||
describe('save', function () {
|
||||
describe('with confirmOverwrite', function () {
|
||||
|
||||
function stubConfirmOverwrite() {
|
||||
window.confirm = sinon.stub().returns(true);
|
||||
sinon.stub(esAdminStub, 'create').returns(BluebirdPromise.reject({ status : 409 }));
|
||||
sinon.stub(esDataStub, 'create').returns(BluebirdPromise.reject({ status : 409 }));
|
||||
}
|
||||
|
||||
describe('when true', function () {
|
||||
it('requests confirmation and updates on yes response', function () {
|
||||
stubESResponse(getMockedDocResponse('myId'));
|
||||
return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
|
||||
stubConfirmOverwrite();
|
||||
|
||||
savedObject.lastSavedTitle = 'original title';
|
||||
savedObject.title = 'new title';
|
||||
return savedObject.save({ confirmOverwrite : true })
|
||||
.then(() => {
|
||||
expect(window.confirm.called).to.be(true);
|
||||
expect(savedObject.id).to.be('myId');
|
||||
expect(savedObject.isSaving).to.be(false);
|
||||
expect(savedObject.lastSavedTitle).to.be('new title');
|
||||
expect(savedObject.title).to.be('new title');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('does not update on no response', function () {
|
||||
stubESResponse(getMockedDocResponse('HI'));
|
||||
return createInitializedSavedObject({ type: 'dashboard', id: 'HI' }).then(savedObject => {
|
||||
window.confirm = sinon.stub().returns(false);
|
||||
sinon.stub(esAdminStub, 'create').returns(BluebirdPromise.reject({ status : 409 }));
|
||||
sinon.stub(esDataStub, 'create').returns(BluebirdPromise.reject({ status : 409 }));
|
||||
|
||||
savedObject.lastSavedTitle = 'original title';
|
||||
savedObject.title = 'new title';
|
||||
return savedObject.save({ confirmOverwrite : true })
|
||||
.then(() => {
|
||||
expect(savedObject.id).to.be('HI');
|
||||
expect(savedObject.isSaving).to.be(false);
|
||||
expect(savedObject.lastSavedTitle).to.be('original title');
|
||||
expect(savedObject.title).to.be('new title');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('handles doIndex failures', function () {
|
||||
stubESResponse(getMockedDocResponse('myId'));
|
||||
return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
|
||||
stubConfirmOverwrite();
|
||||
esAdminStub.index.restore();
|
||||
esDataStub.index.restore();
|
||||
|
||||
sinon.stub(esAdminStub, 'index').returns(BluebirdPromise.reject());
|
||||
sinon.stub(esDataStub, 'index').returns(BluebirdPromise.reject());
|
||||
|
||||
return savedObject.save({ confirmOverwrite : true })
|
||||
.then(() => {
|
||||
expect(true).to.be(false); // Force failure, the save should not succeed.
|
||||
})
|
||||
.catch(() => {
|
||||
expect(window.confirm.called).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('when false does not request overwrite', function () {
|
||||
const mockDocResponse = getMockedDocResponse('myId');
|
||||
stubESResponse(mockDocResponse);
|
||||
return createInitializedSavedObject({ type: 'dashboard', id: 'myId' }).then(savedObject => {
|
||||
sinon.stub(DocSource.prototype, 'doCreate', function () {
|
||||
return BluebirdPromise.reject({ 'origError' : { 'status' : 409 } });
|
||||
});
|
||||
|
||||
stubConfirmOverwrite();
|
||||
return savedObject.save({ confirmOverwrite : false })
|
||||
.then(() => {
|
||||
expect(window.confirm.called).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(' with copyOnSave', function () {
|
||||
it('as true creates a copy on save success', function () {
|
||||
const mockDocResponse = getMockedDocResponse('myId');
|
||||
|
|
|
@ -258,13 +258,53 @@ export default function SavedObjectFactory(esAdmin, kbnIndex, Promise, Private,
|
|||
return esAdmin.indices.refresh({ index: kbnIndex });
|
||||
}
|
||||
|
||||
/**
|
||||
* An error message to be used when the user rejects a confirm overwrite.
|
||||
* @type {string}
|
||||
*/
|
||||
const OVERWRITE_REJECTED = 'Overwrite confirmation was rejected';
|
||||
|
||||
/**
|
||||
* Attempts to create the current object using the serialized source. If an object already
|
||||
* exists, a warning message requests an overwrite confirmation.
|
||||
* @param source - serialized version of this object (return value from this.serialize())
|
||||
* What will be indexed into elasticsearch.
|
||||
* @returns {Promise} - A promise that is resolved with the objects id if the object is
|
||||
* successfully indexed. If the overwrite confirmation was rejected, an error is thrown with
|
||||
* a confirmRejected = true parameter so that case can be handled differently than
|
||||
* a create or index error.
|
||||
* @resolved {String} - The id of the doc
|
||||
*/
|
||||
const createSource = (source) => {
|
||||
return docSource.doCreate(source)
|
||||
.catch((err) => {
|
||||
// record exists, confirm overwriting
|
||||
if (_.get(err, 'origError.status') === 409) {
|
||||
const confirmMessage = `Are you sure you want to overwrite ${this.title}?`;
|
||||
|
||||
return safeConfirm(confirmMessage)
|
||||
.then(() => docSource.doIndex(source))
|
||||
.catch(() => Promise.reject(new Error(OVERWRITE_REJECTED)));
|
||||
}
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} SaveOptions
|
||||
* @property {boolean} confirmOverwrite - If true, attempts to create the source so it
|
||||
* can confirm an overwrite if a document with the id already exists.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Saves this object.
|
||||
*
|
||||
* @param {SaveOptions} saveOptions?
|
||||
* @return {Promise}
|
||||
* @resolved {String} - The id of the doc
|
||||
*/
|
||||
this.save = () => {
|
||||
this.save = (saveOptions = {}) => {
|
||||
// Save the original id in case the save fails.
|
||||
const originalId = this.id;
|
||||
// Read https://github.com/elastic/kibana/issues/9056 and
|
||||
|
@ -285,7 +325,8 @@ export default function SavedObjectFactory(esAdmin, kbnIndex, Promise, Private,
|
|||
const source = this.serialize();
|
||||
|
||||
this.isSaving = true;
|
||||
return docSource.doIndex(source)
|
||||
const doSave = saveOptions.confirmOverwrite ? createSource(source) : docSource.doIndex(source);
|
||||
return doSave
|
||||
.then((id) => { this.id = id; })
|
||||
.then(refreshIndex)
|
||||
.then(() => {
|
||||
|
@ -296,6 +337,7 @@ export default function SavedObjectFactory(esAdmin, kbnIndex, Promise, Private,
|
|||
.catch((err) => {
|
||||
this.isSaving = false;
|
||||
this.id = originalId;
|
||||
if (err && err.message === OVERWRITE_REJECTED) return;
|
||||
return Promise.reject(err);
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue