Skip to content

Commit

Permalink
Disable checking for conflicts by default when copying objects
Browse files Browse the repository at this point in the history
Made this change on the client side and the server side.
  • Loading branch information
jportner committed Nov 18, 2020
1 parent 9606ced commit a72f52a
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 26 deletions.
20 changes: 15 additions & 5 deletions docs/api/spaces-management/copy_saved_objects.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@ You can request to overwrite any objects that already exist in the target space
(Optional, boolean) When set to `true`, all saved objects related to the specified saved objects will also be copied into the target
spaces. The default value is `false`.

`createNewCopies`::
(Optional, boolean) Creates new copies of saved objects, regenerates each object ID, and resets the origin. When used, potential conflict
errors are avoided. The default value is `true`.
+
NOTE: This cannot be used with the `overwrite` option.

`overwrite`::
(Optional, boolean) When set to `true`, all conflicts are automatically overidden. When a saved object with a matching `type` and `id`
exists in the target space, that version is replaced with the version from the source space. The default value is `false`.
+
NOTE: This cannot be used with the `createNewCopies` option.

[role="child_attributes"]
[[spaces-api-copy-saved-objects-response-body]]
Expand Down Expand Up @@ -128,8 +136,7 @@ $ curl -X POST api/spaces/_copy_saved_objects
"id": "my-dashboard"
}],
"spaces": ["marketing"],
"includeReferences": true,
"createNewcopies": true
"includeReferences": true
}
----
// KIBANA
Expand Down Expand Up @@ -193,7 +200,8 @@ $ curl -X POST api/spaces/_copy_saved_objects
"id": "my-dashboard"
}],
"spaces": ["marketing"],
"includeReferences": true
"includeReferences": true,
"createNewCopies": false
}
----
// KIBANA
Expand Down Expand Up @@ -254,7 +262,8 @@ $ curl -X POST api/spaces/_copy_saved_objects
"id": "my-dashboard"
}],
"spaces": ["marketing", "sales"],
"includeReferences": true
"includeReferences": true,
"createNewCopies": false
}
----
// KIBANA
Expand Down Expand Up @@ -405,7 +414,8 @@ $ curl -X POST api/spaces/_copy_saved_objects
"id": "my-dashboard"
}],
"spaces": ["marketing"],
"includeReferences": true
"includeReferences": true,
"createNewCopies": false
}
----
// KIBANA
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Execute the <<spaces-api-copy-saved-objects,copy saved objects to space API>>, w
`includeReferences`::
(Optional, boolean) When set to `true`, all saved objects related to the specified saved objects are copied into the target spaces. The `includeReferences` must be the same values used during the failed <<spaces-api-copy-saved-objects, copy saved objects to space API>> operation. The default value is `false`.

`createNewCopies`::
(Optional, boolean) Creates new copies of the saved objects, regenerates each object ID, and resets the origin. When enabled during the
initial copy, also enable when resolving copy errors. The default value is `true`.

`retries`::
(Required, object) The retry operations to attempt, which can specify how to resolve different types of errors. Object keys represent the
target space IDs.
Expand Down Expand Up @@ -148,6 +152,7 @@ $ curl -X POST api/spaces/_resolve_copy_saved_objects_errors
"id": "my-dashboard"
}],
"includeReferences": true,
"createNewCopies": false,
"retries": {
"sales": [
{
Expand Down Expand Up @@ -246,6 +251,7 @@ $ curl -X POST api/spaces/_resolve_copy_saved_objects_errors
"id": "my-dashboard"
}],
"includeReferences": true,
"createNewCopies": false,
"retries": {
"marketing": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { mountWithIntl } from '@kbn/test/jest';
import { CopyModeControl, CopyModeControlProps } from './copy_mode_control';

describe('CopyModeControl', () => {
const initialValues = { createNewCopies: false, overwrite: true }; // some test cases below make assumptions based on these initial values
const initialValues = { createNewCopies: true, overwrite: true }; // some test cases below make assumptions based on these initial values
const updateSelection = jest.fn();

const getOverwriteRadio = (wrapper: ReactWrapper) =>
Expand All @@ -34,21 +34,23 @@ describe('CopyModeControl', () => {
const wrapper = mountWithIntl(<CopyModeControl {...props} />);

expect(updateSelection).not.toHaveBeenCalled();
const { createNewCopies } = initialValues;
// need to disable `createNewCopies` first
getCreateNewCopiesDisabled(wrapper).simulate('change');
const createNewCopies = false;

getOverwriteDisabled(wrapper).simulate('change');
expect(updateSelection).toHaveBeenNthCalledWith(1, { createNewCopies, overwrite: false });
expect(updateSelection).toHaveBeenNthCalledWith(2, { createNewCopies, overwrite: false });

getOverwriteEnabled(wrapper).simulate('change');
expect(updateSelection).toHaveBeenNthCalledWith(2, { createNewCopies, overwrite: true });
expect(updateSelection).toHaveBeenNthCalledWith(3, { createNewCopies, overwrite: true });
});

it('should disable the Overwrite switch when `createNewCopies` is enabled', async () => {
it('should enable the Overwrite switch when `createNewCopies` is disabled', async () => {
const wrapper = mountWithIntl(<CopyModeControl {...props} />);

expect(getOverwriteRadio(wrapper).prop('disabled')).toBe(false);
getCreateNewCopiesEnabled(wrapper).simulate('change');
expect(getOverwriteRadio(wrapper).prop('disabled')).toBe(true);
getCreateNewCopiesDisabled(wrapper).simulate('change');
expect(getOverwriteRadio(wrapper).prop('disabled')).toBe(false);
});

it('should allow the user to toggle `createNewCopies`', async () => {
Expand All @@ -57,10 +59,10 @@ describe('CopyModeControl', () => {
expect(updateSelection).not.toHaveBeenCalled();
const { overwrite } = initialValues;

getCreateNewCopiesEnabled(wrapper).simulate('change');
expect(updateSelection).toHaveBeenNthCalledWith(1, { createNewCopies: true, overwrite });

getCreateNewCopiesDisabled(wrapper).simulate('change');
expect(updateSelection).toHaveBeenNthCalledWith(2, { createNewCopies: false, overwrite });
expect(updateSelection).toHaveBeenNthCalledWith(1, { createNewCopies: false, overwrite });

getCreateNewCopiesEnabled(wrapper).simulate('change');
expect(updateSelection).toHaveBeenNthCalledWith(2, { createNewCopies: true, overwrite });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui';
import { Space } from '../../../common/model/space';
import { findTestSubject } from '@kbn/test/jest';
import { SelectableSpacesControl } from './selectable_spaces_control';
import { CopyModeControl } from './copy_mode_control';
import { act } from '@testing-library/react';
import { ProcessingCopyToSpace } from './processing_copy_to_space';
import { spacesManagerMock } from '../../spaces_manager/mocks';
Expand Down Expand Up @@ -289,7 +290,7 @@ describe('CopyToSpaceFlyout', () => {
[{ type: savedObjectToCopy.type, id: savedObjectToCopy.id }],
['space-1', 'space-2'],
true,
false,
true, // `createNewCopies` is enabled by default
true
);

Expand Down Expand Up @@ -376,14 +377,25 @@ describe('CopyToSpaceFlyout', () => {
spaceSelector.props().onChange(['space-1', 'space-2']);
});

const startButton = findTestSubject(wrapper, 'cts-initiate-button');
// Change copy mode to check for conflicts
const copyModeControl = wrapper.find(CopyModeControl);
copyModeControl.find('input[id="createNewCopiesDisabled"]').simulate('change');

await act(async () => {
const startButton = findTestSubject(wrapper, 'cts-initiate-button');
startButton.simulate('click');
await nextTick();
wrapper.update();
});

expect(mockSpacesManager.copySavedObjects).toHaveBeenCalledWith(
[{ type: savedObjectToCopy.type, id: savedObjectToCopy.id }],
['space-1', 'space-2'],
true,
false, // `createNewCopies` is disabled
true
);

expect(wrapper.find(CopyToSpaceForm)).toHaveLength(0);
expect(wrapper.find(ProcessingCopyToSpace)).toHaveLength(1);

Expand Down Expand Up @@ -429,7 +441,7 @@ describe('CopyToSpaceFlyout', () => {
],
},
true,
false
false // `createNewCopies` is disabled
);

expect(onClose).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -545,7 +557,7 @@ describe('CopyToSpaceFlyout', () => {
],
},
true,
false
true // `createNewCopies` is enabled by default
);

expect(onClose).toHaveBeenCalledTimes(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface Props {
}

const INCLUDE_RELATED_DEFAULT = true;
const CREATE_NEW_COPIES_DEFAULT = false;
const CREATE_NEW_COPIES_DEFAULT = true;
const OVERWRITE_ALL_DEFAULT = true;

export const CopySavedObjectsToSpaceFlyout = (props: Props) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ export class SpacesManager {
objects,
spaces,
includeReferences,
...(createNewCopies ? { createNewCopies } : { overwrite }),
createNewCopies,
...(createNewCopies ? { overwrite: false } : { overwrite }), // ignore the overwrite option if createNewCopies is enabled
}),
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function initCopyToSpacesApi(deps: ExternalRouteDeps) {
),
includeReferences: schema.boolean({ defaultValue: false }),
overwrite: schema.boolean({ defaultValue: false }),
createNewCopies: schema.boolean({ defaultValue: false }),
createNewCopies: schema.boolean({ defaultValue: true }),
},
{
validate: (object) => {
Expand Down Expand Up @@ -151,7 +151,7 @@ export function initCopyToSpacesApi(deps: ExternalRouteDeps) {
}
),
includeReferences: schema.boolean({ defaultValue: false }),
createNewCopies: schema.boolean({ defaultValue: false }),
createNewCopies: schema.boolean({ defaultValue: true }),
}),
},
},
Expand Down
32 changes: 32 additions & 0 deletions x-pack/test/functional/apps/spaces/copy_saved_objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default function spaceSelectorFunctonalTests({
await PageObjects.copySavedObjectsToSpace.openCopyToSpaceFlyoutForObject('A Dashboard');

await PageObjects.copySavedObjectsToSpace.setupForm({
createNewCopies: false,
overwrite: true,
destinationSpaceId,
});
Expand Down Expand Up @@ -80,6 +81,7 @@ export default function spaceSelectorFunctonalTests({
await PageObjects.copySavedObjectsToSpace.openCopyToSpaceFlyoutForObject('A Dashboard');

await PageObjects.copySavedObjectsToSpace.setupForm({
createNewCopies: false,
overwrite: false,
destinationSpaceId,
});
Expand Down Expand Up @@ -116,12 +118,42 @@ export default function spaceSelectorFunctonalTests({
await PageObjects.copySavedObjectsToSpace.finishCopy();
});

it('avoids conflicts when createNewCopies is enabled', async () => {
const destinationSpaceId = 'sales';

await PageObjects.copySavedObjectsToSpace.openCopyToSpaceFlyoutForObject('A Dashboard');

await PageObjects.copySavedObjectsToSpace.setupForm({
createNewCopies: true,
overwrite: false,
destinationSpaceId,
});

await PageObjects.copySavedObjectsToSpace.startCopy();

// Wait for successful copy
await testSubjects.waitForDeleted(`cts-summary-indicator-loading-${destinationSpaceId}`);
await testSubjects.existOrFail(`cts-summary-indicator-success-${destinationSpaceId}`);

const summaryCounts = await PageObjects.copySavedObjectsToSpace.getSummaryCounts();

expect(summaryCounts).to.eql({
success: 3,
pending: 0,
skipped: 0,
errors: 0,
});

await PageObjects.copySavedObjectsToSpace.finishCopy();
});

it('allows a dashboard to be copied to the marketing space, with circular references', async () => {
const destinationSpaceId = 'marketing';

await PageObjects.copySavedObjectsToSpace.openCopyToSpaceFlyoutForObject('Dashboard Foo');

await PageObjects.copySavedObjectsToSpace.setupForm({
createNewCopies: false,
overwrite: true,
destinationSpaceId,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,24 @@ export function CopySavedObjectsToSpacePageProvider({
},

async setupForm({
createNewCopies,
overwrite,
destinationSpaceId,
}: {
createNewCopies?: boolean;
overwrite?: boolean;
destinationSpaceId: string;
}) {
if (createNewCopies && overwrite) {
throw new Error('createNewCopies and overwrite options cannot be used together');
}
if (!createNewCopies) {
const form = await testSubjects.find('copy-to-space-form');
// a radio button consists of a div tag that contains an input, a div, and a label
// we can't click the input directly, need to click the label
const label = await form.findByCssSelector('label[for="createNewCopiesDisabled"]');
await label.click();
}
if (!overwrite) {
const radio = await testSubjects.find('cts-copyModeControl-overwriteRadioGroup');
// a radio button consists of a div tag that contains an input, a div, and a label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: [destination],
includeReferences: false,
createNewCopies: false,
overwrite: false,
})
.expect(tests.noConflictsWithoutReferences.statusCode)
Expand All @@ -625,6 +626,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: [destination],
includeReferences: true,
createNewCopies: false,
overwrite: false,
})
.expect(tests.noConflictsWithReferences.statusCode)
Expand All @@ -643,6 +645,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: [destination],
includeReferences: true,
createNewCopies: false,
overwrite: true,
})
.expect(tests.withConflictsOverwriting.statusCode)
Expand All @@ -661,6 +664,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: [destination],
includeReferences: true,
createNewCopies: false,
overwrite: false,
})
.expect(tests.withConflictsWithoutOverwriting.statusCode)
Expand All @@ -678,6 +682,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: [conflictDestination, noConflictDestination],
includeReferences: true,
createNewCopies: false,
overwrite: true,
})
.expect(tests.multipleSpaces.statusCode)
Expand Down Expand Up @@ -710,6 +715,7 @@ export function copyToSpaceTestSuiteFactory(
objects: [dashboardObject],
spaces: ['non_existent_space'],
includeReferences: false,
createNewCopies: false,
overwrite: true,
})
.expect(tests.nonExistentSpace.statusCode)
Expand All @@ -720,6 +726,7 @@ export function copyToSpaceTestSuiteFactory(
[false, true].forEach((overwrite) => {
const spaces = ['space_2'];
const includeReferences = false;
const createNewCopies = false;
describe(`multi-namespace types with overwrite=${overwrite}`, () => {
before(() => esArchiver.load('saved_objects/spaces'));
after(() => esArchiver.unload('saved_objects/spaces'));
Expand All @@ -730,7 +737,7 @@ export function copyToSpaceTestSuiteFactory(
return supertest
.post(`${getUrlPrefix(spaceId)}/api/spaces/_copy_saved_objects`)
.auth(user.username, user.password)
.send({ objects, spaces, includeReferences, overwrite })
.send({ objects, spaces, includeReferences, createNewCopies, overwrite })
.expect(statusCode)
.then(response);
});
Expand Down
Loading

0 comments on commit a72f52a

Please sign in to comment.