Ensure migrations run on saved object import (#34793)

* Ensure migrations run on saved object import

* Fix broken tests

* Fix ESLint errors
This commit is contained in:
Mike Côté 2019-04-09 15:18:49 -04:00 committed by GitHub
parent d259e1f258
commit fcd60ca0d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 115 additions and 3 deletions

View file

@ -43,6 +43,7 @@ describe('collectSavedObjects()', () => {
Array [
Object {
"foo": true,
"migrationVersion": Object {},
},
]
`);
@ -60,6 +61,7 @@ Array [
Array [
Object {
"foo": true,
"migrationVersion": Object {},
},
]
`);

View file

@ -44,6 +44,10 @@ export async function collectSavedObjects(
createFilterStream<SavedObject>(obj => !!obj),
createLimitStream(objectLimit),
createFilterStream<SavedObject>(obj => (filter ? filter(obj) : true)),
createMapStream((obj: SavedObject) => {
// Ensure migrations execute on every saved object
return Object.assign({ migrationVersion: {} }, obj);
}),
createConcatStream([]),
])) as SavedObject[];
}

View file

@ -124,6 +124,7 @@ Object {
"title": "My Index Pattern",
},
"id": "1",
"migrationVersion": Object {},
"references": Array [],
"type": "index-pattern",
},
@ -132,6 +133,7 @@ Object {
"title": "My Search",
},
"id": "2",
"migrationVersion": Object {},
"references": Array [],
"type": "search",
},
@ -140,6 +142,7 @@ Object {
"title": "My Visualization",
},
"id": "3",
"migrationVersion": Object {},
"references": Array [],
"type": "visualization",
},
@ -148,6 +151,7 @@ Object {
"title": "My Dashboard",
},
"id": "4",
"migrationVersion": Object {},
"references": Array [],
"type": "dashboard",
},
@ -200,6 +204,7 @@ Object {
"title": "My Index Pattern",
},
"id": "1",
"migrationVersion": Object {},
"references": Array [],
"type": "index-pattern",
},
@ -208,6 +213,7 @@ Object {
"title": "My Search",
},
"id": "2",
"migrationVersion": Object {},
"references": Array [],
"type": "search",
},
@ -216,6 +222,7 @@ Object {
"title": "My Visualization",
},
"id": "3",
"migrationVersion": Object {},
"references": Array [],
"type": "visualization",
},
@ -224,6 +231,7 @@ Object {
"title": "My Dashboard",
},
"id": "4",
"migrationVersion": Object {},
"references": Array [],
"type": "dashboard",
},

View file

@ -141,6 +141,7 @@ Object {
"title": "My Visualization",
},
"id": "3",
"migrationVersion": Object {},
"references": Array [],
"type": "visualization",
},
@ -196,6 +197,7 @@ Object {
"title": "My Index Pattern",
},
"id": "1",
"migrationVersion": Object {},
"references": Array [],
"type": "index-pattern",
},
@ -260,6 +262,7 @@ Object {
"title": "My Dashboard",
},
"id": "4",
"migrationVersion": Object {},
"references": Array [
Object {
"id": "13",

View file

@ -77,6 +77,47 @@ describe('POST /api/saved_objects/_import', () => {
expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0);
});
test('defaults migrationVersion to empty object', async () => {
const request = {
method: 'POST',
url: '/api/saved_objects/_import',
payload: [
'--EXAMPLE',
'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
'Content-Type: application/ndjson',
'',
'{"type":"index-pattern","id":"my-pattern","attributes":{"title":"my-pattern-*"}}',
'--EXAMPLE--',
].join('\r\n'),
headers: {
'content-Type': 'multipart/form-data; boundary=EXAMPLE',
},
};
savedObjectsClient.find.mockResolvedValueOnce({ saved_objects: [] });
savedObjectsClient.bulkCreate.mockResolvedValueOnce({
saved_objects: [
{
type: 'index-pattern',
id: 'my-pattern',
attributes: {
title: 'my-pattern-*',
},
},
],
});
const { payload, statusCode } = await server.inject(request);
const response = JSON.parse(payload);
expect(statusCode).toBe(200);
expect(response).toEqual({
success: true,
successCount: 1,
});
expect(savedObjectsClient.bulkCreate.mock.calls).toHaveLength(1);
const firstBulkCreateCallArray = savedObjectsClient.bulkCreate.mock.calls[0][0];
expect(firstBulkCreateCallArray).toHaveLength(1);
expect(firstBulkCreateCallArray[0].migrationVersion).toEqual({});
});
test('imports an index pattern and dashboard', async () => {
// NOTE: changes to this scenario should be reflected in the docs
const request = {

View file

@ -77,7 +77,48 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(0);
});
test('retries importin a dashboard', async () => {
test('defaults migrationVersion to empty object', async () => {
const request = {
method: 'POST',
url: '/api/saved_objects/_resolve_import_errors',
payload: [
'--EXAMPLE',
'Content-Disposition: form-data; name="file"; filename="export.ndjson"',
'Content-Type: application/ndjson',
'',
'{"type":"dashboard","id":"my-dashboard","attributes":{"title":"Look at my dashboard"}}',
'--EXAMPLE',
'Content-Disposition: form-data; name="retries"',
'',
'[{"type":"dashboard","id":"my-dashboard"}]',
'--EXAMPLE--',
].join('\r\n'),
headers: {
'content-Type': 'multipart/form-data; boundary=EXAMPLE',
},
};
savedObjectsClient.bulkCreate.mockResolvedValueOnce({
saved_objects: [
{
type: 'dashboard',
id: 'my-dashboard',
attributes: {
title: 'Look at my dashboard',
},
},
],
});
const { payload, statusCode } = await server.inject(request);
const response = JSON.parse(payload);
expect(statusCode).toBe(200);
expect(response).toEqual({ success: true, successCount: 1 });
expect(savedObjectsClient.bulkCreate.mock.calls).toHaveLength(1);
const firstBulkCreateCallArray = savedObjectsClient.bulkCreate.mock.calls[0][0];
expect(firstBulkCreateCallArray).toHaveLength(1);
expect(firstBulkCreateCallArray[0].migrationVersion).toEqual({});
});
test('retries importing a dashboard', async () => {
// NOTE: changes to this scenario should be reflected in the docs
const request = {
method: 'POST',
@ -123,6 +164,7 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
"title": "Look at my dashboard",
},
"id": "my-dashboard",
"migrationVersion": Object {},
"type": "dashboard",
},
],
@ -185,6 +227,7 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
"title": "Look at my dashboard",
},
"id": "my-dashboard",
"migrationVersion": Object {},
"type": "dashboard",
},
],
@ -266,6 +309,7 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
"title": "Look at my visualization",
},
"id": "my-vis",
"migrationVersion": Object {},
"references": Array [
Object {
"id": "existing",

View file

@ -113,6 +113,7 @@ export default function ({ getService }) {
type: 'visualization',
attributes: {
title: 'My favorite vis',
visState: '{}',
},
references: [
{
@ -230,6 +231,7 @@ export default function ({ getService }) {
type: 'visualization',
attributes: {
title: 'My favorite vis',
visState: '{}',
},
references: [
{

View file

@ -112,7 +112,11 @@ export function importTestSuiteFactory(es: any, esArchiver: any, supertest: Supe
await supertest
.post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import`)
.auth(user.username, user.password)
.attach('file', Buffer.from(JSON.stringify(data), 'utf8'), 'export.ndjson')
.attach(
'file',
Buffer.from(data.map(obj => JSON.stringify(obj)).join('\n'), 'utf8'),
'export.ndjson'
)
.expect(tests.default.statusCode)
.then(tests.default.response);
});
@ -131,7 +135,11 @@ export function importTestSuiteFactory(es: any, esArchiver: any, supertest: Supe
.post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import`)
.query({ overwrite: true })
.auth(user.username, user.password)
.attach('file', Buffer.from(JSON.stringify(data), 'utf8'), 'export.ndjson')
.attach(
'file',
Buffer.from(data.map(obj => JSON.stringify(obj)).join('\n'), 'utf8'),
'export.ndjson'
)
.expect(tests.unknownType.statusCode)
.then(tests.unknownType.response);
});