From 5f1d254bfac07e81b4c1410a9965518d63632435 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Wed, 18 Nov 2020 20:11:57 -0500 Subject: [PATCH] Disable checking for conflicts by default when importing objects Made this change on the client side and the server side. --- docs/api/saved-objects/import.asciidoc | 14 ++--- .../resolve_import_errors.asciidoc | 8 +-- .../server/saved_objects/routes/import.ts | 2 +- .../routes/integration_tests/import.test.ts | 15 ++++- .../resolve_import_errors.test.ts | 9 ++- .../routes/resolve_import_errors.ts | 2 +- .../public/lib/import_file.ts | 5 +- .../public/lib/resolve_import_errors.ts | 2 +- .../__snapshots__/flyout.test.tsx.snap | 10 +-- .../objects_table/components/flyout.test.tsx | 10 +-- .../objects_table/components/flyout.tsx | 2 +- .../components/import_mode_control.tsx | 3 +- .../apis/saved_objects/import.ts | 8 ++- .../saved_objects/resolve_import_errors.js | 9 +++ test/functional/apps/dashboard/time_zones.js | 5 +- .../apps/management/_import_objects.ts | 63 +++++++++++-------- .../management/_mgmt_import_saved_objects.js | 5 +- .../management/saved_objects_page.ts | 56 +++++++++++++++-- .../common/suites/import.ts | 8 +-- .../common/suites/resolve_import_errors.ts | 4 +- 20 files changed, 165 insertions(+), 75 deletions(-) diff --git a/docs/api/saved-objects/import.asciidoc b/docs/api/saved-objects/import.asciidoc index 4df2f07bfcf41ea..5cf04e63bcf5c09 100644 --- a/docs/api/saved-objects/import.asciidoc +++ b/docs/api/saved-objects/import.asciidoc @@ -23,14 +23,14 @@ experimental[] Create sets of {kib} saved objects from a file created by the exp ==== Query parameters `createNewCopies`:: - (Optional, boolean) Creates copies of saved objects, regenerates each object ID, and resets the origin. When used, potential conflict - errors are avoided. + (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) Overwrites saved objects when they already exist. When used, potential conflict errors are automatically resolved by - overwriting the destination object. + overwriting the destination object. The default value is `false`. + NOTE: This cannot be used with the `createNewCopies` option. @@ -80,7 +80,7 @@ Import an index pattern and dashboard: [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_import?createNewCopies=true -H "kbn-xsrf: true" --form file=@file.ndjson +$ curl -X POST api/saved_objects/_import -H "kbn-xsrf: true" --form file=@file.ndjson -------------------------------------------------- // KIBANA @@ -132,7 +132,7 @@ Import an index pattern and dashboard: [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_import -H "kbn-xsrf: true" --form file=@file.ndjson +$ curl -X POST api/saved_objects/_import?createNewCopies=false -H "kbn-xsrf: true" --form file=@file.ndjson -------------------------------------------------- // KIBANA @@ -181,7 +181,7 @@ Import an index pattern, visualization, *Canvas* workpad, and dashboard that inc [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_import -H "kbn-xsrf: true" --form file=@file.ndjson +$ curl -X POST api/saved_objects/_import?createNewCopies=false -H "kbn-xsrf: true" --form file=@file.ndjson -------------------------------------------------- // KIBANA @@ -292,7 +292,7 @@ Import a visualization and dashboard when the index pattern for the visualizatio [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_import -H "kbn-xsrf: true" --form file=@file.ndjson +$ curl -X POST api/saved_objects/_import?createNewCopies=false -H "kbn-xsrf: true" --form file=@file.ndjson -------------------------------------------------- // KIBANA diff --git a/docs/api/saved-objects/resolve_import_errors.asciidoc b/docs/api/saved-objects/resolve_import_errors.asciidoc index 13d4ac9bbf7d0c0..314560ac8737e8c 100644 --- a/docs/api/saved-objects/resolve_import_errors.asciidoc +++ b/docs/api/saved-objects/resolve_import_errors.asciidoc @@ -31,8 +31,8 @@ To resolve errors, you can: ==== Query parameters `createNewCopies`:: - (Optional, boolean) Creates copies of the saved objects, regenerates each object ID, and resets the origin. When enabled during the - initial import, also enable when resolving import errors. + (Optional, boolean) Creates new copies of the saved objects, regenerates each object ID, and resets the origin. When enabled during the + initial import, also enable when resolving import errors. The default value is `true`. [[saved-objects-api-resolve-import-errors-request-body]] ==== Request body @@ -103,7 +103,7 @@ Resolve conflict errors for an index pattern, visualization, and *Canvas* workpa [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_resolve_import_errors -H "kbn-xsrf: true" --form file=@file.ndjson --form retries='[{"type":"index-pattern","id":"my-pattern","overwrite":true},{"type":"visualization","id":"my-vis","overwrite":true,"destinationId":"another-vis"},{"type":"canvas","id":"my-canvas","overwrite":true,"destinationId":"yet-another-canvas"},{"type":"dashboard","id":"my-dashboard"}]' +$ curl -X POST api/saved_objects/_resolve_import_errors?createNewCopies=false -H "kbn-xsrf: true" --form file=@file.ndjson --form retries='[{"type":"index-pattern","id":"my-pattern","overwrite":true},{"type":"visualization","id":"my-vis","overwrite":true,"destinationId":"another-vis"},{"type":"canvas","id":"my-canvas","overwrite":true,"destinationId":"yet-another-canvas"},{"type":"dashboard","id":"my-dashboard"}]' -------------------------------------------------- // KIBANA @@ -178,7 +178,7 @@ a search by ignoring it: [source,sh] -------------------------------------------------- -$ curl -X POST api/saved_objects/_resolve_import_errors -H "kbn-xsrf: true" --form file=@file.ndjson --form retries='[{"type":"visualization","id":"my-vis","replaceReferences":[{"type":"index-pattern","from":"my-pattern-*","to":"existing-pattern"}]},{"type":"search","id":"my-search","ignoreMissingReferences":true},{"type":"dashboard","id":"my-dashboard"}]' +$ curl -X POST api/saved_objects/_resolve_import_errors?createNewCopies=false -H "kbn-xsrf: true" --form file=@file.ndjson --form retries='[{"type":"visualization","id":"my-vis","replaceReferences":[{"type":"index-pattern","from":"my-pattern-*","to":"existing-pattern"}]},{"type":"search","id":"my-search","ignoreMissingReferences":true},{"type":"dashboard","id":"my-dashboard"}]' -------------------------------------------------- // KIBANA diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts index 0677edd8a8b959c..55d35d73dd5c4f4 100644 --- a/src/core/server/saved_objects/routes/import.ts +++ b/src/core/server/saved_objects/routes/import.ts @@ -55,7 +55,7 @@ export const registerImportRoute = (router: IRouter, deps: RouteDependencies) => query: schema.object( { overwrite: schema.boolean({ defaultValue: false }), - createNewCopies: schema.boolean({ defaultValue: false }), + createNewCopies: schema.boolean({ defaultValue: true }), }, { validate: (object) => { diff --git a/src/core/server/saved_objects/routes/integration_tests/import.test.ts b/src/core/server/saved_objects/routes/integration_tests/import.test.ts index 6b1bf9dc9aac4dc..6dcc97be8a96aa7 100644 --- a/src/core/server/saved_objects/routes/integration_tests/import.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/import.test.ts @@ -89,6 +89,7 @@ describe(`POST ${URL}`, () => { it('formats successful response and records telemetry data', async () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=BOUNDARY') .send( [ @@ -115,6 +116,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -155,6 +157,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -202,6 +205,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -252,7 +256,8 @@ describe(`POST ${URL}`, () => { }); const result = await supertest(httpSetup.server.listener) - .post(`${URL}?overwrite=true`) + .post(URL) + .query({ createNewCopies: false, overwrite: true }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -300,6 +305,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -363,6 +369,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -432,7 +439,8 @@ describe(`POST ${URL}`, () => { }); const result = await supertest(httpSetup.server.listener) - .post(`${URL}?overwrite=true`) + .post(URL) + .query({ createNewCopies: false, overwrite: true }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -502,7 +510,8 @@ describe(`POST ${URL}`, () => { savedObjectsClient.bulkCreate.mockResolvedValueOnce({ saved_objects: [obj1, obj2] }); const result = await supertest(httpSetup.server.listener) - .post(`${URL}?createNewCopies=true`) + .post(URL) + .query({ createNewCopies: true }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ diff --git a/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts index f408e0081fa4340..8cde5cdd64ab048 100644 --- a/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts @@ -94,6 +94,7 @@ describe(`POST ${URL}`, () => { it('formats successful response and records telemetry data', async () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=BOUNDARY') .send( [ @@ -123,6 +124,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -164,6 +166,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -201,6 +204,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -240,6 +244,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -287,6 +292,7 @@ describe(`POST ${URL}`, () => { const result = await supertest(httpSetup.server.listener) .post(URL) + .query({ createNewCopies: false }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ @@ -344,7 +350,8 @@ describe(`POST ${URL}`, () => { savedObjectsClient.bulkCreate.mockResolvedValueOnce({ saved_objects: [obj1, obj2] }); const result = await supertest(httpSetup.server.listener) - .post(`${URL}?createNewCopies=true`) + .post(URL) + .query({ createNewCopies: true }) .set('content-Type', 'multipart/form-data; boundary=EXAMPLE') .send( [ diff --git a/src/core/server/saved_objects/routes/resolve_import_errors.ts b/src/core/server/saved_objects/routes/resolve_import_errors.ts index 30f5ab217bc1a86..a9b8a4a8d321d24 100644 --- a/src/core/server/saved_objects/routes/resolve_import_errors.ts +++ b/src/core/server/saved_objects/routes/resolve_import_errors.ts @@ -53,7 +53,7 @@ export const registerResolveImportErrorsRoute = (router: IRouter, deps: RouteDep }, validate: { query: schema.object({ - createNewCopies: schema.boolean({ defaultValue: false }), + createNewCopies: schema.boolean({ defaultValue: true }), }), body: schema.object({ file: schema.stream(), diff --git a/src/plugins/saved_objects_management/public/lib/import_file.ts b/src/plugins/saved_objects_management/public/lib/import_file.ts index 84177bda3eb43d0..21ddfd749264662 100644 --- a/src/plugins/saved_objects_management/public/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -33,7 +33,10 @@ export async function importFile( ) { const formData = new FormData(); formData.append('file', file); - const query = createNewCopies ? { createNewCopies } : { overwrite }; + const query = { + createNewCopies, + ...(createNewCopies ? { overwrite: false } : { overwrite }), + }; return await http.post('/api/saved_objects/_import', { body: formData, headers: { diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts index 3084d40b63be64a..ceea1d7fa76a095 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts @@ -83,7 +83,7 @@ async function callResolveImportErrorsApi( const formData = new FormData(); formData.append('file', file); formData.append('retries', JSON.stringify(retries)); - const query = createNewCopies ? { createNewCopies } : {}; + const query = { createNewCopies }; return http.post('/api/saved_objects/_resolve_import_errors', { headers: { // Important to be undefined, it forces proper headers to be set for FormData diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index 3fbacef99806d5a..19a45d80f738dde 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Flyout conflicts should allow conflict resolution 1`] = ` +exports[`Flyout conflicts should allow resolution of missing references 1`] = ` `; -exports[`Flyout conflicts should allow conflict resolution 2`] = ` +exports[`Flyout conflicts should allow resolution of missing references 2`] = ` [MockFunction] { "calls": Array [ Array [ @@ -219,7 +219,7 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` }, "importCount": 0, "importMode": Object { - "createNewCopies": false, + "createNewCopies": true, "overwrite": true, }, "indexPatterns": Array [ @@ -270,7 +270,7 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` } `; -exports[`Flyout legacy conflicts should allow conflict resolution 1`] = ` +exports[`Flyout legacy conflicts should allow resolution of missing references 1`] = ` { })); }); - it('should figure out unmatchedReferences', async () => { + it('should display missing references', async () => { const component = shallowRender(defaultProps); // Ensure all promises resolve @@ -182,7 +182,7 @@ describe('Flyout', () => { await component.instance().import(); expect(importFileMock).toHaveBeenCalledWith(defaultProps.http, mockFile, { - createNewCopies: false, + createNewCopies: true, overwrite: true, }); expect(component.state()).toMatchObject({ @@ -208,7 +208,7 @@ describe('Flyout', () => { }); }); - it('should allow conflict resolution', async () => { + it('should allow resolution of missing references', async () => { const component = shallowRender(defaultProps); // Ensure all promises resolve @@ -334,7 +334,7 @@ describe('Flyout', () => { })); }); - it('should figure out unmatchedReferences', async () => { + it('should display missing references', async () => { const component = shallowRender(defaultProps); // Ensure all promises resolve @@ -389,7 +389,7 @@ describe('Flyout', () => { }); }); - it('should allow conflict resolution', async () => { + it('should allow resolution of missing references', async () => { const component = shallowRender(defaultProps); // Ensure all promises resolve diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index c19bb5d81915869..147b5ea5ed21c12 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -68,7 +68,7 @@ import { OverwriteModal } from './overwrite_modal'; import { ImportModeControl, ImportMode } from './import_mode_control'; import { ImportSummary } from './import_summary'; -const CREATE_NEW_COPIES_DEFAULT = false; +const CREATE_NEW_COPIES_DEFAULT = true; const OVERWRITE_ALL_DEFAULT = true; export interface FlyoutProps { diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx index 4000d620465a8c0..6177559460f3cbf 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx @@ -120,7 +120,7 @@ export const ImportModeControl = ({ options={[overwriteEnabled, overwriteDisabled]} idSelected={overwrite ? overwriteEnabled.id : overwriteDisabled.id} onChange={(id: string) => onChange({ overwrite: id === overwriteEnabled.id })} - disabled={createNewCopies} + disabled={createNewCopies && !isLegacyFile} data-test-subj={'savedObjectsManagement-importModeControl-overwriteRadioGroup'} /> ); @@ -138,6 +138,7 @@ export const ImportModeControl = ({ ), }} + data-test-subj="savedObjectsManagement-importModeControl" > { await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .expect(415) .then((resp) => { expect(resp.body).to.eql({ @@ -74,6 +75,7 @@ export default function ({ getService }: FtrProviderContext) { it('should return errors when conflicts exist', async () => { await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .attach('file', join(__dirname, '../../fixtures/import.ndjson')) .expect(200) .then((resp) => { @@ -92,7 +94,7 @@ export default function ({ getService }: FtrProviderContext) { it('should return 200 when conflicts exist but overwrite is passed in', async () => { await supertest .post('/api/saved_objects/_import') - .query({ overwrite: true }) + .query({ createNewCopies: false, overwrite: true }) .attach('file', join(__dirname, '../../fixtures/import.ndjson')) .expect(200) .then((resp) => { @@ -115,6 +117,7 @@ export default function ({ getService }: FtrProviderContext) { ); await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .attach('file', fileBuffer, 'export.ndjson') .expect(200) .then((resp) => { @@ -144,6 +147,7 @@ export default function ({ getService }: FtrProviderContext) { ); const resp = await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .attach('file', fileBuffer, 'export.ndjson') .expect(200); @@ -178,6 +182,7 @@ export default function ({ getService }: FtrProviderContext) { } await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') .expect(400) .then((resp) => { @@ -211,6 +216,7 @@ export default function ({ getService }: FtrProviderContext) { ]; await supertest .post('/api/saved_objects/_import') + .query({ createNewCopies: false }) .attach('file', Buffer.from(objectsToImport.join('\n'), 'utf8'), 'export.ndjson') .expect(200) .then((resp) => { diff --git a/test/api_integration/apis/saved_objects/resolve_import_errors.js b/test/api_integration/apis/saved_objects/resolve_import_errors.js index 5380e9c3d11d8a4..5458b9a413363ea 100644 --- a/test/api_integration/apis/saved_objects/resolve_import_errors.js +++ b/test/api_integration/apis/saved_objects/resolve_import_errors.js @@ -49,6 +49,7 @@ export default function ({ getService }) { it('should return 200 and import nothing when empty parameters are passed in', async () => { await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field('retries', '[]') .attach('file', join(__dirname, '../../fixtures/import.ndjson')) .expect(200) @@ -63,6 +64,7 @@ export default function ({ getService }) { it('should return 200 and import everything when overwrite parameters contains all objects', async () => { await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field( 'retries', JSON.stringify([ @@ -119,6 +121,7 @@ export default function ({ getService }) { ); await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field('retries', JSON.stringify([{ type: 'wigwags', id: '1' }])) .attach('file', fileBuffer, 'export.ndjson') .expect(200) @@ -146,6 +149,7 @@ export default function ({ getService }) { } await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field('retries', '[]') .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') .expect(400) @@ -176,6 +180,7 @@ export default function ({ getService }) { }; await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field( 'retries', JSON.stringify([ @@ -221,6 +226,7 @@ export default function ({ getService }) { it('should return 200 when skipping all the records', async () => { await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field('retries', '[]') .attach('file', join(__dirname, '../../fixtures/import.ndjson')) .expect(200) @@ -232,6 +238,7 @@ export default function ({ getService }) { it('should return 200 when manually overwriting each object', async () => { await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field( 'retries', JSON.stringify([ @@ -270,6 +277,7 @@ export default function ({ getService }) { it('should return 200 with only one record when overwriting 1 and skipping 1', async () => { await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field( 'retries', JSON.stringify([ @@ -309,6 +317,7 @@ export default function ({ getService }) { }; await supertest .post('/api/saved_objects/_resolve_import_errors') + .query({ createNewCopies: false }) .field( 'retries', JSON.stringify([ diff --git a/test/functional/apps/dashboard/time_zones.js b/test/functional/apps/dashboard/time_zones.js index 800bedb1329788d..c0377f1afc03d75 100644 --- a/test/functional/apps/dashboard/time_zones.js +++ b/test/functional/apps/dashboard/time_zones.js @@ -42,8 +42,9 @@ export default function ({ getService, getPageObjects }) { }); await PageObjects.settings.navigateTo(); await PageObjects.settings.clickKibanaSavedObjects(); - await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', 'timezonetest_6_2_4.json') + await PageObjects.savedObjects.importLegacyFile( + path.join(__dirname, 'exports', 'timezonetest_6_2_4.json'), + false ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.common.navigateToApp('dashboard'); diff --git a/test/functional/apps/management/_import_objects.ts b/test/functional/apps/management/_import_objects.ts index 52428c944d666e5..538c017add1b4dd 100644 --- a/test/functional/apps/management/_import_objects.ts +++ b/test/functional/apps/management/_import_objects.ts @@ -49,7 +49,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should import saved objects', async function () { await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects.ndjson') + path.join(__dirname, 'exports', '_import_objects.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); @@ -72,7 +73,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should import saved objects with circular refs', async function () { await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_circular_refs.ndjson') + path.join(__dirname, 'exports', '_import_objects_circular_refs.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); @@ -90,7 +92,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should provide dialog to allow the importing of saved objects with index pattern conflicts', async function () { await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_conflicts.ndjson') + path.join(__dirname, 'exports', '_import_objects_conflicts.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportConflictsWarning(); await PageObjects.settings.associateIndexPattern( @@ -110,7 +113,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // so that we can override the existing visualization. await PageObjects.savedObjects.importFile( path.join(__dirname, 'exports', '_import_objects_exists.ndjson'), - false + { createNewCopies: false, overwrite: false } ); await PageObjects.savedObjects.checkImportConflictsWarning(); @@ -129,7 +132,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // so that we can be prompted to override the existing visualization. await PageObjects.savedObjects.importFile( path.join(__dirname, 'exports', '_import_objects_exists.ndjson'), - false + { createNewCopies: false, overwrite: false } ); await PageObjects.savedObjects.checkImportConflictsWarning(); @@ -145,13 +148,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should import saved objects linked to saved searches', async function () { await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_saved_search.ndjson') + path.join(__dirname, 'exports', '_import_objects_saved_search.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson') + path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); @@ -163,7 +168,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not import saved objects linked to saved searches when saved search does not exist', async function () { await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson') + path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkNoneImported(); await PageObjects.savedObjects.clickImportDone(); @@ -178,7 +184,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.savedObjects.clickDelete(); await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_with_saved_search.ndjson') + path.join(__dirname, 'exports', '_import_objects_with_saved_search.ndjson'), + { createNewCopies: false, overwrite: true } ); // Wait for all the saves to happen await PageObjects.savedObjects.checkImportConflictsWarning(); @@ -193,7 +200,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should import saved objects with index patterns when index patterns already exists', async () => { // First, import the objects await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson') + path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); @@ -210,7 +218,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Then, import the objects await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson') + path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson'), + { createNewCopies: false, overwrite: true } ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); @@ -235,7 +244,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should import saved objects', async function () { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects.json') ); await PageObjects.savedObjects.checkImportSucceeded(); @@ -246,7 +255,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should provide dialog to allow the importing of saved objects with index pattern conflicts', async function () { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects-conflicts.json') ); await PageObjects.savedObjects.checkImportLegacyWarning(); @@ -266,7 +275,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow the user to override duplicate saved objects', async function () { // This data has already been loaded by the "visualize" esArchive. We'll load it again // so that we can override the existing visualization. - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_exists.json'), false ); @@ -286,7 +295,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow the user to cancel overriding duplicate saved objects', async function () { // This data has already been loaded by the "visualize" esArchive. We'll load it again // so that we can be prompted to override the existing visualization. - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_exists.json'), false ); @@ -306,7 +315,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow the user to confirm overriding multiple duplicate saved objects', async function () { // This data has already been loaded by the "visualize" esArchive. We'll load it again // so that we can override the existing visualization. - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_multiple_exists.json'), false ); @@ -333,7 +342,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should allow the user to confirm overriding multiple duplicate index patterns', async function () { // This data has already been loaded by the "visualize" esArchive. We'll load it again // so that we can override the existing visualization. - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_index_patterns_multiple_exists.json'), false ); @@ -352,13 +361,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should import saved objects linked to saved searches', async function () { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_saved_search.json') ); await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json') ); await PageObjects.savedObjects.checkImportSucceeded(); @@ -370,7 +379,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should not import saved objects linked to saved searches when saved search does not exist', async function () { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json') ); await PageObjects.savedObjects.checkImportFailedWarning(); @@ -383,7 +392,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not import saved objects linked to saved searches when saved search index pattern does not exist', async function () { // First, import the saved search - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_saved_search.json') ); // Wait for all the saves to happen @@ -396,7 +405,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Last, import a saved object connected to the saved search // This should NOT show the conflicts - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json') ); // Wait for all the saves to happen @@ -410,7 +419,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should import saved objects with index patterns when index patterns already exists', async () => { // First, import the objects - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_with_index_patterns.json') ); await PageObjects.savedObjects.clickImportDone(); @@ -421,7 +430,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should preserve index patterns selection when switching between pages', async () => { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_objects_missing_all_index_patterns.json') ); @@ -457,7 +466,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should display an explicit error message when importing object from a higher Kibana version', async () => { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_higher_version.ndjson') ); @@ -474,7 +483,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // see --savedObjects.maxImportPayloadBytes in config file this.tags(['skipCloud']); it('should display an explicit error message when importing a file bigger than allowed', async () => { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_too_big.ndjson') ); @@ -487,7 +496,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should display an explicit error message when importing an invalid file', async () => { - await PageObjects.savedObjects.importFile( + await PageObjects.savedObjects.importLegacyFile( path.join(__dirname, 'exports', '_import_invalid_format.ndjson') ); diff --git a/test/functional/apps/management/_mgmt_import_saved_objects.js b/test/functional/apps/management/_mgmt_import_saved_objects.js index d479a7006d0f88b..c9af4c905543445 100644 --- a/test/functional/apps/management/_mgmt_import_saved_objects.js +++ b/test/functional/apps/management/_mgmt_import_saved_objects.js @@ -41,8 +41,9 @@ export default function ({ getService, getPageObjects }) { it('should import saved objects mgmt', async function () { await PageObjects.settings.clickKibanaSavedObjects(); - await PageObjects.savedObjects.importFile( - path.join(__dirname, 'exports', 'mgmt_import_objects.json') + await PageObjects.savedObjects.importLegacyFile( + path.join(__dirname, 'exports', 'mgmt_import_objects.json'), + false ); await PageObjects.settings.associateIndexPattern( '4c3f3c30-ac94-11e8-a651-614b2788174a', diff --git a/test/functional/page_objects/management/saved_objects_page.ts b/test/functional/page_objects/management/saved_objects_page.ts index f5b95e556e7f1cf..1eb2564145ffacb 100644 --- a/test/functional/page_objects/management/saved_objects_page.ts +++ b/test/functional/page_objects/management/saved_objects_page.ts @@ -44,22 +44,70 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv return await searchBox.getAttribute('value'); } - async importFile(path: string, overwriteAll = true) { + async importFile( + path: string, + options: { + createNewCopies?: boolean; + overwrite?: boolean; + } + ) { + const { createNewCopies, overwrite } = options; + log.debug(`importFile(${path})`); + + log.debug(`Clicking importObjects`); + await testSubjects.click('importObjects'); + await PageObjects.common.setFileInputPath(path); + + if (createNewCopies && overwrite) { + throw new Error('createNewCopies and overwrite options cannot be used together'); + } + + if (!createNewCopies) { + log.debug(`Toggling createNewCopies`); + const form = await testSubjects.find('savedObjectsManagement-importModeControl'); + // 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(); + } else { + log.debug(`Leaving createNewCopies alone`); + } + + if (!overwrite) { + log.debug(`Toggling overwriteAll`); + const radio = await testSubjects.find( + 'savedObjectsManagement-importModeControl-overwriteRadioGroup' + ); + // 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 go up one level and click the parent div + const label = await radio.findByCssSelector('label[for="overwriteDisabled"]'); + await label.click(); + } else { + log.debug(`Leaving overwriteAll alone`); + } + await testSubjects.click('importSavedObjectsImportBtn'); + log.debug(`done importing the file`); + + // Wait for all the saves to happen + await PageObjects.header.waitUntilLoadingHasFinished(); + } + + async importLegacyFile(path: string, overwrite = true) { log.debug(`importFile(${path})`); log.debug(`Clicking importObjects`); await testSubjects.click('importObjects'); await PageObjects.common.setFileInputPath(path); - if (!overwriteAll) { + if (!overwrite) { log.debug(`Toggling overwriteAll`); const radio = await testSubjects.find( 'savedObjectsManagement-importModeControl-overwriteRadioGroup' ); // 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 go up one level and click the parent div - const div = await radio.findByXpath("//div[input[@id='overwriteDisabled']]"); - await div.click(); + const label = await radio.findByCssSelector('label[for="overwriteDisabled"]'); + await label.click(); } else { log.debug(`Leaving overwriteAll alone`); } diff --git a/x-pack/test/saved_object_api_integration/common/suites/import.ts b/x-pack/test/saved_object_api_integration/common/suites/import.ts index f3de6f75b981dad..2d4e06b2c2f838e 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/import.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/import.ts @@ -241,13 +241,9 @@ export function importTestSuiteFactory(es: any, esArchiver: any, supertest: Supe const requestBody = test.request .map((obj) => JSON.stringify({ ...obj, ...attrs })) .join('\n'); - const query = test.overwrite - ? '?overwrite=true' - : test.createNewCopies - ? '?createNewCopies=true' - : ''; await supertest - .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import${query}`) + .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import`) + .query({ createNewCopies: test.createNewCopies, overwrite: test.overwrite }) .auth(user?.username, user?.password) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson') .expect(test.responseStatusCode) diff --git a/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts index 1d616cb7e583216..38c45d66ccc1dc9 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/resolve_import_errors.ts @@ -253,9 +253,9 @@ export function resolveImportErrorsTestSuiteFactory( const requestBody = test.request.objects .map((obj) => JSON.stringify({ ...obj, ...attrs })) .join('\n'); - const query = test.createNewCopies ? '?createNewCopies=true' : ''; await supertest - .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_resolve_import_errors${query}`) + .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_resolve_import_errors`) + .query({ createNewCopies: test.createNewCopies }) .auth(user?.username, user?.password) .field('retries', JSON.stringify(test.request.retries)) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson')