mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Migrations] Add support of deferred migrations (#153117)
* Add deferred migrations parameter. * Update outdated documents query to take into account deferred migrations. * Update outdated documents query to take into account the core migration version. * Update read operations in the saved objects repository to perform deferred migrations.
This commit is contained in:
parent
633444e615
commit
a65cd356aa
75 changed files with 2141 additions and 834 deletions
|
@ -254,6 +254,38 @@ the error should be verbose and informative so that the corrupt document can be
|
|||
|
||||
**WARNING:** Do not attempt to change the `typeMigrationVersion`, `id`, or `type` fields within a migration function, this is not supported.
|
||||
|
||||
### Deferred Migrations
|
||||
Usually, migrations run during the upgrade process, and sometimes that may block it if there is a huge amount of outdated objects.
|
||||
In this case, it is recommended to mark some of the migrations to defer their execution.
|
||||
|
||||
```ts
|
||||
export const dashboardVisualization: SavedObjectsType = {
|
||||
name: 'dashboard_visualization', [1]
|
||||
/** ... */
|
||||
migrations: {
|
||||
// Takes a pre 1.1.0 doc, and converts it to 1.1.0
|
||||
'1.1.0': {
|
||||
deferred: true,
|
||||
transform: migrateDashboardVisualization110,
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
By default, all the migrations are not deferred, and in order to make them so, the `deferred` flag should be explicitly set to `true`.
|
||||
In this case, the documents with only pending deferred migrations will not be migrated during the upgrade process.
|
||||
|
||||
But whenever they are accessed via Saved Object API or repository, all the migrations will be applied to them on the fly:
|
||||
- On read operations, the stored objects remain untouched and only transformed before returning the result.
|
||||
If there are some failures during the migration, an exception or 500 server error will be thrown,
|
||||
so that it is guaranteed that all the returned objects will be up to date.
|
||||
- On write operations, the objects will be migrated to the latest version before writing them.
|
||||
|
||||
In other words, this flag postpones the write operation until the objects are explicitly modified.
|
||||
|
||||
One important notice: if there is a few pending migrations for a document and not all of them can be deferred,
|
||||
the document will be migrated during the upgrade process, and all pending migrations will be applied.
|
||||
|
||||
### Testing Migrations
|
||||
|
||||
Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the <DocLink id="kibDevTutorialTestingPlugins" section="saved-objects-migrations" text="Saved Object migrations section in the plugin testing guide"/>.
|
||||
|
|
|
@ -46,7 +46,15 @@ type ExpectedBulkGetResult = Either<
|
|||
|
||||
export const performBulkGet = async <T>(
|
||||
{ objects, options }: PerformBulkGetParams<T>,
|
||||
{ helpers, allowedTypes, client, serializer, registry, extensions = {} }: ApiExecutionContext
|
||||
{
|
||||
helpers,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
registry,
|
||||
extensions = {},
|
||||
}: ApiExecutionContext
|
||||
): Promise<SavedObjectsBulkResponse<T>> => {
|
||||
const {
|
||||
common: commonHelper,
|
||||
|
@ -192,9 +200,12 @@ export const performBulkGet = async <T>(
|
|||
}
|
||||
|
||||
// @ts-expect-error MultiGetHit._source is optional
|
||||
return getSavedObjectFromSource(registry, type, id, doc, {
|
||||
const document = getSavedObjectFromSource(registry, type, id, doc, {
|
||||
migrationVersionCompatibility,
|
||||
});
|
||||
const migrated = migrator.migrateDocument(document);
|
||||
|
||||
return migrated;
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ export const performBulkResolve = async <T>(
|
|||
helpers,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
extensions = {},
|
||||
} = apiExecutionContext;
|
||||
|
@ -43,6 +44,7 @@ export const performBulkResolve = async <T>(
|
|||
registry,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
getIndexForType: commonHelper.getIndexForType.bind(commonHelper),
|
||||
incrementCounterInternal: (type, id, counterFields, opts = {}) =>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
SavedObjectsErrorHelpers,
|
||||
type SavedObjectsRawDoc,
|
||||
CheckAuthorizationResult,
|
||||
type SavedObject,
|
||||
SavedObjectsRawDocSource,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import {
|
||||
|
@ -48,7 +49,6 @@ export const performFind = async <T = unknown, A = unknown>(
|
|||
allowedTypes: rawAllowedTypes,
|
||||
mappings,
|
||||
client,
|
||||
serializer,
|
||||
migrator,
|
||||
extensions = {},
|
||||
}: ApiExecutionContext
|
||||
|
@ -229,22 +229,32 @@ export const performFind = async <T = unknown, A = unknown>(
|
|||
return SavedObjectsUtils.createEmptyFindResponse<T, A>(options);
|
||||
}
|
||||
|
||||
const result = {
|
||||
...(body.aggregations ? { aggregations: body.aggregations as unknown as A } : {}),
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: body.hits.total,
|
||||
saved_objects: body.hits.hits.map(
|
||||
(hit: estypes.SearchHit<SavedObjectsRawDocSource>): SavedObjectsFindResult => ({
|
||||
...serializerHelper.rawToSavedObject(hit as SavedObjectsRawDoc, {
|
||||
migrationVersionCompatibility,
|
||||
}),
|
||||
score: hit._score!,
|
||||
sort: hit.sort,
|
||||
})
|
||||
),
|
||||
pit_id: body.pit_id,
|
||||
} as SavedObjectsFindResponse<T, A>;
|
||||
let result: SavedObjectsFindResponse<T, A>;
|
||||
try {
|
||||
result = {
|
||||
...(body.aggregations ? { aggregations: body.aggregations as unknown as A } : {}),
|
||||
page,
|
||||
per_page: perPage,
|
||||
total: body.hits.total,
|
||||
saved_objects: body.hits.hits.map(
|
||||
(hit: estypes.SearchHit<SavedObjectsRawDocSource>): SavedObjectsFindResult => ({
|
||||
...(migrator.migrateDocument(
|
||||
serializerHelper.rawToSavedObject(hit as SavedObjectsRawDoc, {
|
||||
migrationVersionCompatibility,
|
||||
})
|
||||
) as SavedObject),
|
||||
score: hit._score!,
|
||||
sort: hit.sort,
|
||||
})
|
||||
),
|
||||
pit_id: body.pit_id,
|
||||
} as typeof result;
|
||||
} catch (error) {
|
||||
throw SavedObjectsErrorHelpers.decorateGeneralError(
|
||||
error,
|
||||
'Failed to migrate document to the latest version.'
|
||||
);
|
||||
}
|
||||
|
||||
if (disableExtensions) {
|
||||
return result;
|
||||
|
|
|
@ -24,7 +24,15 @@ export interface PerformGetParams {
|
|||
|
||||
export const performGet = async <T>(
|
||||
{ type, id, options }: PerformGetParams,
|
||||
{ registry, helpers, allowedTypes, client, serializer, extensions = {} }: ApiExecutionContext
|
||||
{
|
||||
registry,
|
||||
helpers,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
extensions = {},
|
||||
}: ApiExecutionContext
|
||||
): Promise<SavedObject<T>> => {
|
||||
const { common: commonHelper, encryption: encryptionHelper } = helpers;
|
||||
const { securityExtension } = extensions;
|
||||
|
@ -68,12 +76,22 @@ export const performGet = async <T>(
|
|||
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
|
||||
}
|
||||
|
||||
const result = getSavedObjectFromSource<T>(registry, type, id, body, {
|
||||
const document = getSavedObjectFromSource<T>(registry, type, id, body, {
|
||||
migrationVersionCompatibility,
|
||||
});
|
||||
|
||||
let migrated: SavedObject<T>;
|
||||
try {
|
||||
migrated = migrator.migrateDocument(document) as SavedObject<T>;
|
||||
} catch (error) {
|
||||
throw SavedObjectsErrorHelpers.decorateGeneralError(
|
||||
error,
|
||||
'Failed to migrate document to the latest version.'
|
||||
);
|
||||
}
|
||||
|
||||
return encryptionHelper.optionallyDecryptAndRedactSingleResult(
|
||||
result,
|
||||
migrated,
|
||||
authorizationResult?.typeMap
|
||||
);
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
type ISavedObjectTypeRegistry,
|
||||
type SavedObject,
|
||||
SavedObjectsErrorHelpers,
|
||||
type SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import {
|
||||
enforceError,
|
||||
|
@ -38,6 +39,7 @@ import {
|
|||
setupAuthorizeAndRedactInternalBulkResolveSuccess,
|
||||
} from '../../../test_helpers/repository.test.common';
|
||||
import { savedObjectsExtensionsMock } from '../../../mocks/saved_objects_extensions.mock';
|
||||
import { kibanaMigratorMock } from '../../../mocks';
|
||||
|
||||
const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 };
|
||||
const OBJ_TYPE = 'obj-type';
|
||||
|
@ -57,6 +59,7 @@ beforeEach(() => {
|
|||
|
||||
describe('internalBulkResolve', () => {
|
||||
let client: ReturnType<typeof elasticsearchClientMock.createElasticsearchClient>;
|
||||
let migrator: ReturnType<typeof kibanaMigratorMock.create>;
|
||||
let serializer: SavedObjectsSerializer;
|
||||
let incrementCounterInternal: jest.Mock<any, any>;
|
||||
let registry: jest.Mocked<ISavedObjectTypeRegistry>;
|
||||
|
@ -72,11 +75,13 @@ describe('internalBulkResolve', () => {
|
|||
): InternalBulkResolveParams {
|
||||
registry = typeRegistryMock.create();
|
||||
client = elasticsearchClientMock.createElasticsearchClient();
|
||||
migrator = kibanaMigratorMock.create();
|
||||
serializer = new SavedObjectsSerializer(registry);
|
||||
incrementCounterInternal = jest.fn().mockRejectedValue(new Error('increment error')); // mock error to implicitly test that it is caught and swallowed
|
||||
return {
|
||||
registry,
|
||||
allowedTypes: [OBJ_TYPE, ENCRYPTED_TYPE],
|
||||
migrator,
|
||||
client,
|
||||
serializer,
|
||||
getIndexForType: (type: string) => `index-for-${type}`,
|
||||
|
@ -223,7 +228,7 @@ describe('internalBulkResolve', () => {
|
|||
};
|
||||
}
|
||||
|
||||
for (const namespace of [undefined, 'default', 'space-x']) {
|
||||
describe.each([undefined, 'default', 'space-x'])(`with namespace '%s'`, (namespace) => {
|
||||
const expectedNamespaceString = SavedObjectsUtils.namespaceIdToString(namespace);
|
||||
|
||||
it('throws if mget call results in non-ES-originated 404 error', async () => {
|
||||
|
@ -362,7 +367,29 @@ describe('internalBulkResolve', () => {
|
|||
expectConflictResult({ id: '7', alias_target_id: '7-newId', alias_purpose: 'y' }),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
it('migrates the resolved objects', async () => {
|
||||
const objects = [
|
||||
{ type: OBJ_TYPE, id: '1' },
|
||||
{ type: OBJ_TYPE, id: '2' },
|
||||
];
|
||||
const params = setup(objects, { namespace });
|
||||
mockBulkResults({ found: false }, { found: false });
|
||||
mockMgetResults({ found: true }, { found: true });
|
||||
migrator.migrateDocument.mockImplementation(
|
||||
(doc) => `migrated-${doc}` as unknown as SavedObjectUnsanitizedDoc
|
||||
);
|
||||
|
||||
await expect(internalBulkResolve(params)).resolves.toHaveProperty('resolved_objects', [
|
||||
expect.objectContaining({ saved_object: 'migrated-mock-obj-for-1' }),
|
||||
expect.objectContaining({ saved_object: 'migrated-mock-obj-for-2' }),
|
||||
]);
|
||||
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledTimes(2);
|
||||
expect(migrator.migrateDocument).nthCalledWith(1, 'mock-obj-for-1');
|
||||
expect(migrator.migrateDocument).nthCalledWith(2, 'mock-obj-for-2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with encryption extension', () => {
|
||||
const namespace = 'foo';
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
SavedObjectsErrorHelpers,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import {
|
||||
type IKibanaMigrator,
|
||||
LEGACY_URL_ALIAS_TYPE,
|
||||
type LegacyUrlAlias,
|
||||
} from '@kbn/core-saved-objects-base-server-internal';
|
||||
|
@ -59,6 +60,7 @@ const MAX_CONCURRENT_RESOLVE = 10;
|
|||
*/
|
||||
export interface InternalBulkResolveParams {
|
||||
registry: ISavedObjectTypeRegistry;
|
||||
migrator: IKibanaMigrator;
|
||||
allowedTypes: string[];
|
||||
client: RepositoryEsClient;
|
||||
serializer: ISavedObjectsSerializer;
|
||||
|
@ -98,6 +100,7 @@ export async function internalBulkResolve<T>(
|
|||
): Promise<InternalSavedObjectsBulkResolveResponse<T>> {
|
||||
const {
|
||||
registry,
|
||||
migrator,
|
||||
allowedTypes,
|
||||
client,
|
||||
serializer,
|
||||
|
@ -184,10 +187,12 @@ export async function internalBulkResolve<T>(
|
|||
const object = getSavedObjectFromSource<T>(registry, objectType, objectId, doc, {
|
||||
migrationVersionCompatibility,
|
||||
});
|
||||
if (!encryptionExtension?.isEncryptableType(object.type)) {
|
||||
return object;
|
||||
const migrated = migrator.migrateDocument(object) as SavedObject<T>;
|
||||
|
||||
if (!encryptionExtension?.isEncryptableType(migrated.type)) {
|
||||
return migrated;
|
||||
}
|
||||
return encryptionExtension.decryptOrStripResponseAttributes(object);
|
||||
return encryptionExtension.decryptOrStripResponseAttributes(migrated);
|
||||
}
|
||||
|
||||
// map function for pMap below
|
||||
|
@ -211,28 +216,39 @@ export async function internalBulkResolve<T>(
|
|||
const { type, id } = either.value;
|
||||
let result: SavedObjectsResolveResponse<T> | null = null;
|
||||
|
||||
if (foundExactMatch && foundAliasMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, id, exactMatchDoc!),
|
||||
outcome: 'conflict',
|
||||
alias_target_id: aliasInfo!.targetId,
|
||||
alias_purpose: aliasInfo!.purpose,
|
||||
try {
|
||||
if (foundExactMatch && foundAliasMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, id, exactMatchDoc!),
|
||||
outcome: 'conflict',
|
||||
alias_target_id: aliasInfo!.targetId,
|
||||
alias_purpose: aliasInfo!.purpose,
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.CONFLICT);
|
||||
} else if (foundExactMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, id, exactMatchDoc!),
|
||||
outcome: 'exactMatch',
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.EXACT_MATCH);
|
||||
} else if (foundAliasMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, aliasInfo!.targetId, aliasMatchDoc!),
|
||||
outcome: 'aliasMatch',
|
||||
alias_target_id: aliasInfo!.targetId,
|
||||
alias_purpose: aliasInfo!.purpose,
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.ALIAS_MATCH);
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
error: SavedObjectsErrorHelpers.decorateGeneralError(
|
||||
error,
|
||||
'Failed to migrate document to the latest version.'
|
||||
),
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.CONFLICT);
|
||||
} else if (foundExactMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, id, exactMatchDoc!),
|
||||
outcome: 'exactMatch',
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.EXACT_MATCH);
|
||||
} else if (foundAliasMatch) {
|
||||
result = {
|
||||
saved_object: await getSavedObject(type, aliasInfo!.targetId, aliasMatchDoc!),
|
||||
outcome: 'aliasMatch',
|
||||
alias_target_id: aliasInfo!.targetId,
|
||||
alias_purpose: aliasInfo!.purpose,
|
||||
};
|
||||
resolveCounter.recordOutcome(REPOSITORY_RESOLVE_OUTCOME_STATS.ALIAS_MATCH);
|
||||
}
|
||||
|
||||
if (result !== null) {
|
||||
|
|
|
@ -29,6 +29,7 @@ export const performResolve = async <T>(
|
|||
helpers,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
extensions = {},
|
||||
} = apiExecutionContext;
|
||||
|
@ -39,6 +40,7 @@ export const performResolve = async <T>(
|
|||
registry,
|
||||
allowedTypes,
|
||||
client,
|
||||
migrator,
|
||||
serializer,
|
||||
getIndexForType: commonHelper.getIndexForType.bind(commonHelper),
|
||||
incrementCounterInternal: (t, i, counterFields, opts = {}) =>
|
||||
|
|
|
@ -742,6 +742,9 @@ describe('SavedObjectsRepository Security Extension', () => {
|
|||
attributes: doc._source![doc._source!.type],
|
||||
references: [],
|
||||
namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -795,6 +798,9 @@ describe('SavedObjectsRepository Security Extension', () => {
|
|||
attributes: doc._source![doc._source!.type],
|
||||
references: [],
|
||||
namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -849,6 +855,9 @@ describe('SavedObjectsRepository Security Extension', () => {
|
|||
attributes: doc._source![doc._source!.type],
|
||||
references: [],
|
||||
namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1316,7 +1316,9 @@ describe('SavedObjectsRepository', () => {
|
|||
version: encodeHitVersion(doc),
|
||||
attributes: doc._source![type],
|
||||
references: doc._source!.references || [],
|
||||
migrationVersion: doc._source!.migrationVersion,
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
|
||||
it(`returns early for empty objects argument`, async () => {
|
||||
|
@ -1389,6 +1391,24 @@ describe('SavedObjectsRepository', () => {
|
|||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('migrates the fetched documents', async () => {
|
||||
const response = getMockMgetResponse(registry, [obj1, obj2]);
|
||||
client.mget.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(response)
|
||||
);
|
||||
migrator.migrateDocument.mockReturnValue(
|
||||
'migrated' as unknown as ReturnType<typeof migrator.migrateDocument>
|
||||
);
|
||||
|
||||
await expect(bulkGet(repository, [obj1, obj2])).resolves.toHaveProperty('saved_objects', [
|
||||
'migrated',
|
||||
'migrated',
|
||||
]);
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledTimes(2);
|
||||
expect(migrator.migrateDocument).nthCalledWith(1, expect.objectContaining({ id: obj1.id }));
|
||||
expect(migrator.migrateDocument).nthCalledWith(2, expect.objectContaining({ id: obj2.id }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3910,6 +3930,9 @@ describe('SavedObjectsRepository', () => {
|
|||
attributes: doc._source![doc._source!.type],
|
||||
references: [],
|
||||
namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : ['default'],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3937,6 +3960,9 @@ describe('SavedObjectsRepository', () => {
|
|||
attributes: doc._source![doc._source!.type],
|
||||
references: [],
|
||||
namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3952,6 +3978,30 @@ describe('SavedObjectsRepository', () => {
|
|||
await test(HIDDEN_TYPE);
|
||||
await test(['unknownType', HIDDEN_TYPE]);
|
||||
});
|
||||
|
||||
it('migrates the found document', async () => {
|
||||
const noNamespaceSearchResults = generateIndexPatternSearchResults();
|
||||
client.search.mockResolvedValueOnce(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(noNamespaceSearchResults)
|
||||
);
|
||||
migrator.migrateDocument.mockImplementationOnce((doc) => ({ ...doc, migrated: true }));
|
||||
await expect(repository.find({ type })).resolves.toHaveProperty(
|
||||
'saved_objects.0.migrated',
|
||||
true
|
||||
);
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledTimes(
|
||||
noNamespaceSearchResults.hits.hits.length
|
||||
);
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type,
|
||||
id: noNamespaceSearchResults.hits.hits[0]._id.replace(
|
||||
/(index-pattern|config|globalType)\:/,
|
||||
''
|
||||
),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search dsl', () => {
|
||||
|
@ -4272,6 +4322,9 @@ describe('SavedObjectsRepository', () => {
|
|||
},
|
||||
references: [],
|
||||
namespaces: ['default'],
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
managed: expect.any(Boolean),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -4300,6 +4353,20 @@ describe('SavedObjectsRepository', () => {
|
|||
expect(result).toMatchObject({ originId });
|
||||
});
|
||||
});
|
||||
|
||||
it('migrates the fetched document', async () => {
|
||||
migrator.migrateDocument.mockReturnValueOnce(
|
||||
'migrated' as unknown as ReturnType<typeof migrator.migrateDocument>
|
||||
);
|
||||
await expect(getSuccess(client, repository, registry, type, id)).resolves.toBe('migrated');
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledTimes(1);
|
||||
expect(migrator.migrateDocument).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id,
|
||||
type,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
|
|
|
@ -582,6 +582,9 @@ export const expectBulkGetResult = (
|
|||
attributes: doc._source![type],
|
||||
references: doc._source!.references || [],
|
||||
migrationVersion: doc._source!.migrationVersion,
|
||||
managed: expect.any(Boolean),
|
||||
coreMigrationVersion: expect.any(String),
|
||||
typeMigrationVersion: expect.any(String),
|
||||
});
|
||||
|
||||
export const getMockBulkCreateResponse = (
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getOutdatedDocumentsQuery should not select documents if there are no migrations 1`] = `
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getOutdatedDocumentsQuery should select documents with outdated both core and type migration versions 1`] = `
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Array [
|
||||
Object {
|
||||
"term": Object {
|
||||
"type": "dashboard",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"coreMigrationVersion": Object {
|
||||
"lt": "8.8.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "typeMigrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion.dashboard",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion",
|
||||
},
|
||||
},
|
||||
"must_not": Object {
|
||||
"term": Object {
|
||||
"migrationVersion.dashboard": "7.7.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"range": Object {
|
||||
"typeMigrationVersion": Object {
|
||||
"lt": "7.7.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getOutdatedDocumentsQuery should select documents with outdated core migration version 1`] = `
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Array [
|
||||
Object {
|
||||
"term": Object {
|
||||
"type": "dashboard",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"coreMigrationVersion": Object {
|
||||
"lt": "8.8.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getOutdatedDocumentsQuery should select documents with outdated type migration version 1`] = `
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Array [
|
||||
Object {
|
||||
"term": Object {
|
||||
"type": "dashboard",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "typeMigrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion.dashboard",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion",
|
||||
},
|
||||
},
|
||||
"must_not": Object {
|
||||
"term": Object {
|
||||
"migrationVersion.dashboard": "7.7.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"range": Object {
|
||||
"typeMigrationVersion": Object {
|
||||
"lt": "7.7.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -39,17 +39,20 @@ describe('buildActiveMigrations', () => {
|
|||
...parts,
|
||||
});
|
||||
|
||||
const transform = (type: TransformType, version: string): Transform => ({
|
||||
const transform = (type: TransformType, version: string, deferred?: boolean): Transform => ({
|
||||
...(deferred != null ? { deferred } : {}),
|
||||
version,
|
||||
transformType: type,
|
||||
transform: jest.fn(),
|
||||
});
|
||||
|
||||
const expectTransform = (type: TransformType, version: string): Transform => ({
|
||||
version,
|
||||
transformType: type,
|
||||
transform: expect.any(Function),
|
||||
});
|
||||
const expectTransform = (type: TransformType, version: string, deferred?: boolean): Transform =>
|
||||
expect.objectContaining({
|
||||
...(deferred != null ? { deferred } : {}),
|
||||
version,
|
||||
transformType: type,
|
||||
transform: expect.any(Function),
|
||||
});
|
||||
|
||||
const addType = (parts: Partial<SavedObjectsType>) => {
|
||||
typeRegistry.registerType(createType(parts));
|
||||
|
@ -123,7 +126,14 @@ describe('buildActiveMigrations', () => {
|
|||
migrations: {
|
||||
'7.12.0': jest.fn(),
|
||||
'7.16.0': jest.fn(),
|
||||
'8.3.0': jest.fn(),
|
||||
'8.3.0': {
|
||||
transform: jest.fn(),
|
||||
},
|
||||
'8.4.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -131,9 +141,10 @@ describe('buildActiveMigrations', () => {
|
|||
|
||||
expect(Object.keys(migrations).sort()).toEqual(['foo']);
|
||||
expect(migrations.foo.transforms).toEqual([
|
||||
expectTransform(TransformType.Migrate, '7.12.0'),
|
||||
expectTransform(TransformType.Migrate, '7.16.0'),
|
||||
expectTransform(TransformType.Migrate, '8.3.0'),
|
||||
expectTransform(TransformType.Migrate, '7.12.0', false),
|
||||
expectTransform(TransformType.Migrate, '7.16.0', false),
|
||||
expectTransform(TransformType.Migrate, '8.3.0', false),
|
||||
expectTransform(TransformType.Migrate, '8.4.0', true),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -339,4 +350,71 @@ describe('buildActiveMigrations', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('versions', () => {
|
||||
it('returns the latest migrations versions', () => {
|
||||
addType({
|
||||
name: 'foo',
|
||||
migrations: {
|
||||
'7.12.0': jest.fn(),
|
||||
'7.16.0': jest.fn(),
|
||||
'8.3.0': jest.fn(),
|
||||
},
|
||||
});
|
||||
getCoreTransformsMock.mockReturnValue([
|
||||
transform(TransformType.Core, '8.8.0'),
|
||||
transform(TransformType.Core, '8.9.0'),
|
||||
]);
|
||||
getReferenceTransformsMock.mockReturnValue([
|
||||
transform(TransformType.Reference, '7.12.0'),
|
||||
transform(TransformType.Reference, '7.17.3'),
|
||||
]);
|
||||
getConversionTransformsMock.mockReturnValue([
|
||||
transform(TransformType.Convert, '7.14.0'),
|
||||
transform(TransformType.Convert, '7.15.0'),
|
||||
]);
|
||||
|
||||
expect(buildMigrations()).toHaveProperty(
|
||||
'foo.latestVersion',
|
||||
expect.objectContaining({
|
||||
[TransformType.Convert]: '7.15.0',
|
||||
[TransformType.Core]: '8.9.0',
|
||||
[TransformType.Migrate]: '8.3.0',
|
||||
[TransformType.Reference]: '7.17.3',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the latest not deferred migrations versions', () => {
|
||||
addType({
|
||||
name: 'foo',
|
||||
migrations: {
|
||||
'7.12.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(),
|
||||
},
|
||||
'7.16.0': jest.fn(),
|
||||
'8.3.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(),
|
||||
},
|
||||
},
|
||||
});
|
||||
getCoreTransformsMock.mockReturnValue([
|
||||
transform(TransformType.Core, '8.7.0', true),
|
||||
transform(TransformType.Core, '8.8.0'),
|
||||
transform(TransformType.Core, '8.9.0', true),
|
||||
]);
|
||||
|
||||
expect(buildMigrations()).toHaveProperty(
|
||||
'foo.immediateVersion',
|
||||
expect.objectContaining({
|
||||
[TransformType.Core]: '8.8.0',
|
||||
[TransformType.Migrate]: '7.16.0',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,7 +36,6 @@ export function buildActiveMigrations({
|
|||
convertVersion?: string;
|
||||
log: Logger;
|
||||
}): ActiveMigrations {
|
||||
const coreTransforms = getCoreTransforms();
|
||||
const referenceTransforms = getReferenceTransforms(typeRegistry);
|
||||
|
||||
return typeRegistry.getAllTypes().reduce((migrations, type) => {
|
||||
|
@ -46,7 +45,6 @@ export function buildActiveMigrations({
|
|||
type,
|
||||
log,
|
||||
kibanaVersion,
|
||||
coreTransforms,
|
||||
referenceTransforms,
|
||||
});
|
||||
|
||||
|
@ -64,13 +62,11 @@ export function buildActiveMigrations({
|
|||
const buildTypeTransforms = ({
|
||||
type,
|
||||
log,
|
||||
coreTransforms,
|
||||
referenceTransforms,
|
||||
}: {
|
||||
type: SavedObjectsType;
|
||||
kibanaVersion: string;
|
||||
log: Logger;
|
||||
coreTransforms: Transform[];
|
||||
referenceTransforms: Transform[];
|
||||
}): TypeTransforms => {
|
||||
const migrationsMap =
|
||||
|
@ -79,11 +75,13 @@ const buildTypeTransforms = ({
|
|||
const migrationTransforms = Object.entries(migrationsMap ?? {}).map<Transform>(
|
||||
([version, transform]) => ({
|
||||
version,
|
||||
deferred: !_.isFunction(transform) && !!transform.deferred,
|
||||
transform: convertMigrationFunction(version, type, transform, log),
|
||||
transformType: TransformType.Migrate,
|
||||
})
|
||||
);
|
||||
|
||||
const coreTransforms = getCoreTransforms({ log, type });
|
||||
const modelVersionTransforms = getModelVersionTransforms({ log, typeDefinition: type });
|
||||
|
||||
const conversionTransforms = getConversionTransforms(type);
|
||||
|
@ -96,6 +94,16 @@ const buildTypeTransforms = ({
|
|||
].sort(transformComparator);
|
||||
|
||||
return {
|
||||
immediateVersion: _.chain(transforms)
|
||||
.groupBy('transformType')
|
||||
.mapValues((items) =>
|
||||
_.chain(items)
|
||||
.filter(({ deferred }) => !deferred)
|
||||
.last()
|
||||
.get('version')
|
||||
.value()
|
||||
)
|
||||
.value() as Record<TransformType, string>,
|
||||
latestVersion: _.chain(transforms)
|
||||
.groupBy('transformType')
|
||||
.mapValues((items) => _.last(items)?.version)
|
||||
|
|
|
@ -635,7 +635,7 @@ describe('DocumentMigrator', () => {
|
|||
),
|
||||
});
|
||||
migrator.prepareMigrations();
|
||||
expect(migrator.migrationVersion).toEqual({
|
||||
expect(migrator.getMigrationVersion()).toEqual({
|
||||
aaa: '10.4.0',
|
||||
bbb: '3.2.3',
|
||||
ccc: '11.0.0',
|
||||
|
@ -643,6 +643,47 @@ describe('DocumentMigrator', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('extracts the latest non-deferred migration version info', () => {
|
||||
const migrator = new DocumentMigrator({
|
||||
...testOpts(),
|
||||
typeRegistry: createRegistry({
|
||||
name: 'aaa',
|
||||
migrations: {
|
||||
'1.2.3': (doc: SavedObjectUnsanitizedDoc) => doc,
|
||||
'2.2.1': (doc: SavedObjectUnsanitizedDoc) => doc,
|
||||
'10.4.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: (doc: SavedObjectUnsanitizedDoc) => doc,
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
migrator.prepareMigrations();
|
||||
expect(migrator.getMigrationVersion({ includeDeferred: false })).toHaveProperty(
|
||||
'aaa',
|
||||
'2.2.1'
|
||||
);
|
||||
});
|
||||
|
||||
test('extracts the latest core migration version info', () => {
|
||||
const migrator = new DocumentMigrator({
|
||||
...testOpts(),
|
||||
typeRegistry: createRegistry({
|
||||
name: 'aaa',
|
||||
migrations: {
|
||||
'1.2.3': (doc: SavedObjectUnsanitizedDoc) => doc,
|
||||
'2.2.1': (doc: SavedObjectUnsanitizedDoc) => doc,
|
||||
},
|
||||
}),
|
||||
});
|
||||
migrator.prepareMigrations();
|
||||
expect(migrator.getMigrationVersion({ migrationType: 'core' })).toHaveProperty(
|
||||
'aaa',
|
||||
'8.8.0'
|
||||
);
|
||||
});
|
||||
|
||||
describe('conversion to multi-namespace type', () => {
|
||||
it('assumes documents w/ undefined typeMigrationVersion and correct coreMigrationVersion are up to date', () => {
|
||||
const migrator = new DocumentMigrator({
|
||||
|
|
|
@ -28,6 +28,20 @@ interface DocumentMigratorOptions {
|
|||
log: Logger;
|
||||
}
|
||||
|
||||
interface MigrationVersionParams {
|
||||
/**
|
||||
* Include deferred migrations in the migrationVersion.
|
||||
* @default true
|
||||
*/
|
||||
includeDeferred?: boolean;
|
||||
|
||||
/**
|
||||
* Migration type to use in the migrationVersion.
|
||||
* @default 'type'
|
||||
*/
|
||||
migrationType?: 'core' | 'type';
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages transformations of individual documents.
|
||||
*/
|
||||
|
@ -35,19 +49,19 @@ export interface VersionedTransformer {
|
|||
/**
|
||||
* Migrates a document to its latest version.
|
||||
*/
|
||||
migrate: (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc;
|
||||
migrate(doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc;
|
||||
/**
|
||||
* Migrates a document to the latest version and applies type conversions if applicable.
|
||||
* Also returns any additional document(s) that may have been created during the transformation process.
|
||||
*/
|
||||
migrateAndConvert: (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc[];
|
||||
migrateAndConvert(doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc[];
|
||||
/**
|
||||
* Converts a document down to the specified version.
|
||||
*/
|
||||
transformDown: (
|
||||
transformDown(
|
||||
doc: SavedObjectUnsanitizedDoc,
|
||||
options: { targetTypeVersion: string }
|
||||
) => SavedObjectUnsanitizedDoc;
|
||||
): SavedObjectUnsanitizedDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,30 +82,39 @@ export class DocumentMigrator implements VersionedTransformer {
|
|||
*/
|
||||
constructor(options: DocumentMigratorOptions) {
|
||||
this.options = options;
|
||||
this.migrate = (...args) => this.constructor.prototype.migrate.apply(this, args);
|
||||
this.migrateAndConvert = (...args) =>
|
||||
this.constructor.prototype.migrateAndConvert.apply(this, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest version of each migrate-able property.
|
||||
* Gets the latest pending version of each type.
|
||||
* Some migration objects won't have a latest migration version (they only contain reference transforms that are applied from other types).
|
||||
*/
|
||||
public get migrationVersion(): SavedObjectsMigrationVersion {
|
||||
public getMigrationVersion({
|
||||
includeDeferred = true,
|
||||
migrationType = 'type',
|
||||
}: MigrationVersionParams = {}): SavedObjectsMigrationVersion {
|
||||
if (!this.migrations) {
|
||||
throw new Error('Migrations are not ready. Make sure prepareMigrations is called first.');
|
||||
}
|
||||
|
||||
return Object.entries(this.migrations).reduce((acc, [prop, { latestVersion }]) => {
|
||||
// some migration objects won't have a latest migration version (they only contain reference transforms that are applied from other types)
|
||||
const latestMigrationVersion = maxVersion(latestVersion.migrate, latestVersion.convert);
|
||||
if (latestMigrationVersion) {
|
||||
return { ...acc, [prop]: latestMigrationVersion };
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
return Object.entries(this.migrations).reduce(
|
||||
(acc, [type, { latestVersion, immediateVersion }]) => {
|
||||
const version = includeDeferred ? latestVersion : immediateVersion;
|
||||
const latestMigrationVersion =
|
||||
migrationType === 'core' ? version.core : maxVersion(version.migrate, version.convert);
|
||||
|
||||
return latestMigrationVersion ? { ...acc, [type]: latestMigrationVersion } : acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares active migrations and document transformer function.
|
||||
*/
|
||||
public prepareMigrations = () => {
|
||||
public prepareMigrations() {
|
||||
const { typeRegistry, kibanaVersion, log, convertVersion } = this.options;
|
||||
this.migrations = buildActiveMigrations({
|
||||
typeRegistry,
|
||||
|
@ -99,31 +122,31 @@ export class DocumentMigrator implements VersionedTransformer {
|
|||
log,
|
||||
convertVersion,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates a document to the latest version.
|
||||
*/
|
||||
public migrate = (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc => {
|
||||
public migrate(doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc {
|
||||
const { document } = this.transform(doc);
|
||||
|
||||
return document;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates a document to the latest version and applies type conversions if applicable. Also returns any additional document(s) that may
|
||||
* have been created during the transformation process.
|
||||
*/
|
||||
public migrateAndConvert = (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc[] => {
|
||||
public migrateAndConvert(doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc[] {
|
||||
const { document, additionalDocs } = this.transform(doc, { convertNamespaceTypes: true });
|
||||
|
||||
return [document, ...additionalDocs];
|
||||
};
|
||||
}
|
||||
|
||||
public transformDown = (
|
||||
public transformDown(
|
||||
doc: SavedObjectUnsanitizedDoc,
|
||||
options: { targetTypeVersion: string }
|
||||
): SavedObjectUnsanitizedDoc => {
|
||||
): SavedObjectUnsanitizedDoc {
|
||||
if (!this.migrations) {
|
||||
throw new Error('Migrations are not ready. Make sure prepareMigrations is called first.');
|
||||
}
|
||||
|
@ -136,7 +159,7 @@ export class DocumentMigrator implements VersionedTransformer {
|
|||
});
|
||||
const { document } = pipeline.run();
|
||||
return document;
|
||||
};
|
||||
}
|
||||
|
||||
private transform(
|
||||
doc: SavedObjectUnsanitizedDoc,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isFunction } from 'lodash';
|
||||
import {
|
||||
ISavedObjectTypeRegistry,
|
||||
SavedObjectsType,
|
||||
|
@ -16,16 +17,25 @@ import {
|
|||
LEGACY_URL_ALIAS_TYPE,
|
||||
LegacyUrlAlias,
|
||||
} from '@kbn/core-saved-objects-base-server-internal';
|
||||
import { Logger } from '@kbn/logging';
|
||||
import { migrations as coreMigrationsMap } from './migrations';
|
||||
import { type Transform, TransformType } from './types';
|
||||
import { convertMigrationFunction } from './utils';
|
||||
|
||||
/**
|
||||
* Returns all available core transforms for all object types.
|
||||
*/
|
||||
export function getCoreTransforms(): Transform[] {
|
||||
export function getCoreTransforms({
|
||||
type,
|
||||
log,
|
||||
}: {
|
||||
type: SavedObjectsType;
|
||||
log: Logger;
|
||||
}): Transform[] {
|
||||
return Object.entries(coreMigrationsMap).map<Transform>(([version, transform]) => ({
|
||||
version,
|
||||
transform,
|
||||
deferred: !isFunction(transform) && !!transform.deferred,
|
||||
transform: convertMigrationFunction(version, type, transform, log),
|
||||
transformType: TransformType.Core,
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -6,17 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { flow } from 'lodash';
|
||||
import get from 'lodash/fp/get';
|
||||
import { TransformFn } from '../types';
|
||||
import type { SavedObjectMigrationMap } from '@kbn/core-saved-objects-server';
|
||||
import { mergeSavedObjectMigrations } from '@kbn/core-saved-objects-utils-server';
|
||||
import { transformMigrationVersion } from './transform_migration_version';
|
||||
import { transformSetManagedDefault } from './transform_set_managed_default';
|
||||
|
||||
export const migrations = {
|
||||
'8.8.0': flow(
|
||||
transformMigrationVersion,
|
||||
// extract transformedDoc from TransformResult as input to next transform
|
||||
get('transformedDoc'),
|
||||
transformSetManagedDefault
|
||||
),
|
||||
} as Record<string, TransformFn>;
|
||||
export const migrations: SavedObjectMigrationMap = {
|
||||
'8.8.0': mergeSavedObjectMigrations(transformMigrationVersion, transformSetManagedDefault),
|
||||
};
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { unary } from 'lodash';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { transformMigrationVersion } from './transform_migration_version';
|
||||
|
||||
const transform = unary(SavedObjectsUtils.getMigrationFunction(transformMigrationVersion));
|
||||
|
||||
describe('transformMigrationVersion', () => {
|
||||
it('should extract the correct version from the `migrationVersion` property', () => {
|
||||
expect(
|
||||
transformMigrationVersion({
|
||||
transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
|
@ -20,12 +24,12 @@ describe('transformMigrationVersion', () => {
|
|||
previous: '2.0.0',
|
||||
},
|
||||
})
|
||||
).toHaveProperty('transformedDoc.typeMigrationVersion', '1.0.0');
|
||||
).toHaveProperty('typeMigrationVersion', '1.0.0');
|
||||
});
|
||||
|
||||
it('should remove the original `migrationVersion` property', () => {
|
||||
expect(
|
||||
transformMigrationVersion({
|
||||
transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
|
@ -34,27 +38,27 @@ describe('transformMigrationVersion', () => {
|
|||
previous: '2.0.0',
|
||||
},
|
||||
})
|
||||
).not.toHaveProperty('transformedDoc.migrationVersion');
|
||||
).not.toHaveProperty('migrationVersion');
|
||||
});
|
||||
|
||||
it('should not add `typeMigrationVersion` if there is no `migrationVersion`', () => {
|
||||
expect(
|
||||
transformMigrationVersion({
|
||||
transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
})
|
||||
).not.toHaveProperty('transformedDoc.typeMigrationVersion');
|
||||
).not.toHaveProperty('typeMigrationVersion');
|
||||
});
|
||||
|
||||
it('should add empty `typeMigrationVersion` if there is no related value in `migrationVersion`', () => {
|
||||
expect(
|
||||
transformMigrationVersion({
|
||||
transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
migrationVersion: {},
|
||||
})
|
||||
).toHaveProperty('transformedDoc.typeMigrationVersion', '');
|
||||
).toHaveProperty('typeMigrationVersion', '');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { TransformFn } from '../types';
|
||||
import type { SavedObjectMigration } from '@kbn/core-saved-objects-server';
|
||||
|
||||
export const transformMigrationVersion: TransformFn = ({ migrationVersion, ...doc }) => {
|
||||
return {
|
||||
transformedDoc: {
|
||||
...doc,
|
||||
...(migrationVersion ? { typeMigrationVersion: migrationVersion[doc.type] ?? '' } : {}),
|
||||
},
|
||||
additionalDocs: [],
|
||||
};
|
||||
export const transformMigrationVersion: SavedObjectMigration = {
|
||||
// @todo Remove when deferred migrations are publicly available.
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: ({ migrationVersion, ...doc }) => ({
|
||||
...doc,
|
||||
...(migrationVersion ? { typeMigrationVersion: migrationVersion[doc.type] ?? '' } : {}),
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -6,35 +6,39 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { unary } from 'lodash';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { transformSetManagedDefault } from './transform_set_managed_default';
|
||||
|
||||
const transform = unary(SavedObjectsUtils.getMigrationFunction(transformSetManagedDefault));
|
||||
|
||||
describe('transformAddManaged', () => {
|
||||
it('should add managed if not defined', () => {
|
||||
expect(
|
||||
transformSetManagedDefault({
|
||||
transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
})
|
||||
).toHaveProperty('transformedDoc.managed');
|
||||
).toHaveProperty('managed');
|
||||
});
|
||||
it('should not change managed if already defined', () => {
|
||||
const docWithManagedFalse = transformSetManagedDefault({
|
||||
const docWithManagedFalse = transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
managed: false,
|
||||
});
|
||||
const docWithManagedTrue = transformSetManagedDefault({
|
||||
const docWithManagedTrue = transform({
|
||||
id: 'a',
|
||||
attributes: {},
|
||||
type: 'something',
|
||||
managed: true,
|
||||
});
|
||||
[docWithManagedFalse, docWithManagedTrue].forEach((doc) => {
|
||||
expect(doc.transformedDoc.managed).toBeDefined();
|
||||
expect(doc.managed).toBeDefined();
|
||||
});
|
||||
expect(docWithManagedFalse.transformedDoc.managed).not.toBeTruthy();
|
||||
expect(docWithManagedTrue.transformedDoc.managed).toBeTruthy();
|
||||
expect(docWithManagedFalse.managed).not.toBeTruthy();
|
||||
expect(docWithManagedTrue.managed).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server';
|
||||
import type { SavedObjectMigration } from '@kbn/core-saved-objects-server';
|
||||
|
||||
export const transformSetManagedDefault = (doc: SavedObjectUnsanitizedDoc) => ({
|
||||
transformedDoc: { ...doc, managed: doc.managed ?? false },
|
||||
additionalDocs: [],
|
||||
});
|
||||
export const transformSetManagedDefault: SavedObjectMigration = {
|
||||
// @todo Remove when deferred migrations are publicly available.
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: ({ managed, ...doc }) => ({ ...doc, managed: managed ?? false }),
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ describe('DocumentMigratorPipeline', () => {
|
|||
|
||||
return {
|
||||
transforms,
|
||||
immediateVersion: latestVersions(versions),
|
||||
latestVersion: latestVersions(versions),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -47,6 +47,7 @@ describe('DocumentMigratorPipeline', () => {
|
|||
|
||||
return {
|
||||
transforms,
|
||||
immediateVersion: latestVersions(versions),
|
||||
latestVersion: latestVersions(versions),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,7 +19,15 @@ export interface ActiveMigrations {
|
|||
* Structure containing all the required info to perform a type's conversion
|
||||
*/
|
||||
export interface TypeTransforms {
|
||||
/** Derived from the related transforms */
|
||||
/**
|
||||
* Latest non-deferred version for each transform type.
|
||||
* This is the version that will be used to query outdated documents.
|
||||
*/
|
||||
immediateVersion: Record<TransformType, string>;
|
||||
/**
|
||||
* Latest version for each transform type, including deferred transforms.
|
||||
* This is the version that will be used to perform the migration.
|
||||
*/
|
||||
latestVersion: Record<TransformType, string>;
|
||||
/** Ordered list of transforms registered for the type **/
|
||||
transforms: Transform[];
|
||||
|
@ -37,6 +45,8 @@ export interface Transform {
|
|||
transform: TransformFn;
|
||||
/** The (optional) downward transformation function */
|
||||
transformDown?: TransformFn;
|
||||
/** Whether this transform is deferred */
|
||||
deferred?: boolean;
|
||||
}
|
||||
|
||||
export enum TransformType {
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isFunction } from 'lodash';
|
||||
import Semver from 'semver';
|
||||
import {
|
||||
SavedObjectMigrationFn,
|
||||
import type {
|
||||
SavedObjectMigration,
|
||||
SavedObjectsType,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
|
@ -31,7 +32,7 @@ const TRANSFORM_PRIORITY = [
|
|||
export function convertMigrationFunction(
|
||||
version: string,
|
||||
type: SavedObjectsType,
|
||||
migrationFn: SavedObjectMigrationFn,
|
||||
migration: SavedObjectMigration,
|
||||
log: Logger
|
||||
): TransformFn {
|
||||
const context = Object.freeze({
|
||||
|
@ -43,7 +44,8 @@ export function convertMigrationFunction(
|
|||
|
||||
return function tryTransformDoc(doc: SavedObjectUnsanitizedDoc) {
|
||||
try {
|
||||
const result = migrationFn(doc, context);
|
||||
const transformFn = isFunction(migration) ? migration : migration.transform;
|
||||
const result = transformFn(doc, context);
|
||||
|
||||
// A basic check to help migration authors detect basic errors
|
||||
// (e.g. forgetting to return the transformed doc)
|
||||
|
|
|
@ -70,15 +70,41 @@ describe('validateTypeMigrations', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('validates the migration function', () => {
|
||||
it('throws on the invalid migration type', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
convertToMultiNamespaceTypeVersion: '3.1.1',
|
||||
namespaceType: 'multiple',
|
||||
migrations: { '1.2.3': 23 as any },
|
||||
});
|
||||
|
||||
expect(() => validate({ type })).toThrow(/expected a function, but got 23/i);
|
||||
expect(() => validate({ type })).toThrow(/expected a function or an object/i);
|
||||
});
|
||||
|
||||
it('throws on the invalid migration object', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
migrations: {
|
||||
'1.2.3': {
|
||||
deferred: false,
|
||||
transform: 23 as any,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type })).toThrow(/expected a function or an object/i);
|
||||
});
|
||||
|
||||
it('validates the migration object', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
migrations: {
|
||||
'1.2.3': {
|
||||
deferred: false,
|
||||
transform: jest.fn(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type })).not.toThrow();
|
||||
});
|
||||
|
||||
describe('when switchToModelVersionAt is specified', () => {
|
||||
|
|
|
@ -49,9 +49,9 @@ export function validateTypeMigrations({
|
|||
`Migrations map for type ${type.name} should be an object like { '2.0.0': (doc) => doc }.`
|
||||
);
|
||||
|
||||
Object.entries(migrationMap).forEach(([version, fn]) => {
|
||||
Object.entries(migrationMap).forEach(([version, migration]) => {
|
||||
assertValidSemver(kibanaVersion, version, type.name);
|
||||
assertValidTransform(fn, version, type.name);
|
||||
assertValidTransform(migration, version, type.name);
|
||||
if (type.switchToModelVersionAt && Semver.gte(version, type.switchToModelVersionAt)) {
|
||||
throw new Error(
|
||||
`Migration for type ${type.name} for version ${version} registered after switchToModelVersionAt (${type.switchToModelVersionAt})`
|
||||
|
@ -169,9 +169,14 @@ const assertValidConvertToMultiNamespaceType = (
|
|||
}
|
||||
};
|
||||
|
||||
const assertValidTransform = (fn: any, version: string, type: string) => {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error(`Invalid migration ${type}.${version}: expected a function, but got ${fn}.`);
|
||||
const assertValidTransform = (migration: any, version: string, type: string) => {
|
||||
if (
|
||||
!(typeof migration === 'object' && typeof migration.transform === 'function') &&
|
||||
typeof migration !== 'function'
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid migration ${type}.${version}: expected a function or an object, but got ${migration}.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getOutdatedDocumentsQuery } from './get_outdated_documents_query';
|
||||
|
||||
describe('getOutdatedDocumentsQuery', () => {
|
||||
it.each`
|
||||
coreMigrationVersionPerType | migrationVersionPerType | case
|
||||
${{}} | ${{}} | ${'should not select documents if there are no migrations'}
|
||||
${{ dashboard: '8.8.0' }} | ${{}} | ${'should select documents with outdated core migration version'}
|
||||
${{}} | ${{ dashboard: '7.7.0' }} | ${'should select documents with outdated type migration version'}
|
||||
${{ dashboard: '8.8.0' }} | ${{ dashboard: '7.7.0' }} | ${'should select documents with outdated both core and type migration versions'}
|
||||
`('$case', ({ coreMigrationVersionPerType, migrationVersionPerType }) => {
|
||||
expect(
|
||||
getOutdatedDocumentsQuery({
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common';
|
||||
|
||||
export interface OutdatedDocumentsQueryParams {
|
||||
coreMigrationVersionPerType: SavedObjectsMigrationVersion;
|
||||
migrationVersionPerType: SavedObjectsMigrationVersion;
|
||||
}
|
||||
|
||||
export function getOutdatedDocumentsQuery({
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
}: OutdatedDocumentsQueryParams): QueryDslQueryContainer {
|
||||
const types = [
|
||||
...new Set([
|
||||
...Object.keys(coreMigrationVersionPerType),
|
||||
...Object.keys(migrationVersionPerType),
|
||||
]).values(),
|
||||
];
|
||||
return {
|
||||
bool: {
|
||||
should: types.map((type) => ({
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { type } },
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
...(coreMigrationVersionPerType[type]
|
||||
? [
|
||||
{
|
||||
range: {
|
||||
coreMigrationVersion: { lt: coreMigrationVersionPerType[type] },
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(migrationVersionPerType[type]
|
||||
? [
|
||||
{
|
||||
bool: {
|
||||
must_not: [
|
||||
{ exists: { field: 'typeMigrationVersion' } },
|
||||
{ exists: { field: `migrationVersion.${type}` } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must: { exists: { field: 'migrationVersion' } },
|
||||
must_not: {
|
||||
term: { [`migrationVersion.${type}`]: migrationVersionPerType[type] },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
range: { typeMigrationVersion: { lt: migrationVersionPerType[type] } },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
},
|
||||
};
|
||||
}
|
|
@ -18,6 +18,8 @@ import {
|
|||
import type { Logger } from '@kbn/logging';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { createInitialState, type CreateInitialStateParams } from './initial_state';
|
||||
import * as getOutdatedDocumentsQueryModule from './get_outdated_documents_query';
|
||||
import { getOutdatedDocumentsQuery } from './get_outdated_documents_query';
|
||||
|
||||
const mockLogger = loggingSystemMock.create();
|
||||
|
||||
|
@ -40,6 +42,7 @@ const createInitialStateCommonParams = {
|
|||
dynamic: 'strict',
|
||||
properties: { my_type: { properties: { title: { type: 'text' } } } },
|
||||
} as IndexMapping,
|
||||
coreMigrationVersionPerType: {},
|
||||
migrationVersionPerType: {},
|
||||
indexPrefix: '.kibana_task_manager',
|
||||
migrationsConfig,
|
||||
|
@ -347,10 +350,13 @@ describe('createInitialState', () => {
|
|||
).toEqual(true);
|
||||
});
|
||||
it('returns state with an outdatedDocumentsQuery', () => {
|
||||
jest.spyOn(getOutdatedDocumentsQueryModule, 'getOutdatedDocumentsQuery');
|
||||
|
||||
expect(
|
||||
createInitialState({
|
||||
...createInitialStateParams,
|
||||
preMigrationScript: "ctx._id = ctx._source.type + ':' + ctx._id",
|
||||
coreMigrationVersionPerType: {},
|
||||
migrationVersionPerType: { my_dashboard: '7.10.1', my_viz: '8.0.0' },
|
||||
}).outdatedDocumentsQuery
|
||||
).toMatchInlineSnapshot(`
|
||||
|
@ -368,6 +374,22 @@ describe('createInitialState', () => {
|
|||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "typeMigrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion.my_dashboard",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Object {
|
||||
|
@ -383,19 +405,10 @@ describe('createInitialState', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"typeMigrationVersion": "7.10.1",
|
||||
},
|
||||
},
|
||||
],
|
||||
"range": Object {
|
||||
"typeMigrationVersion": Object {
|
||||
"lt": "7.10.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -415,6 +428,22 @@ describe('createInitialState', () => {
|
|||
Object {
|
||||
"bool": Object {
|
||||
"should": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "typeMigrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion.my_viz",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must": Object {
|
||||
|
@ -430,19 +459,10 @@ describe('createInitialState', () => {
|
|||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"must_not": Array [
|
||||
Object {
|
||||
"exists": Object {
|
||||
"field": "migrationVersion",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"term": Object {
|
||||
"typeMigrationVersion": "8.0.0",
|
||||
},
|
||||
},
|
||||
],
|
||||
"range": Object {
|
||||
"typeMigrationVersion": Object {
|
||||
"lt": "8.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -455,6 +475,10 @@ describe('createInitialState', () => {
|
|||
},
|
||||
}
|
||||
`);
|
||||
expect(getOutdatedDocumentsQuery).toHaveBeenCalledWith({
|
||||
coreMigrationVersionPerType: {},
|
||||
migrationVersionPerType: { my_dashboard: '7.10.1', my_viz: '8.0.0' },
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes the `discardUnknownObjects` flag to false if the flag is not provided in the config', () => {
|
||||
|
|
|
@ -7,28 +7,29 @@
|
|||
*/
|
||||
|
||||
import * as Option from 'fp-ts/Option';
|
||||
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { DocLinksServiceStart } from '@kbn/core-doc-links-server';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common';
|
||||
import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server';
|
||||
import type {
|
||||
IndexMapping,
|
||||
IndexTypesMap,
|
||||
SavedObjectsMigrationConfigType,
|
||||
} from '@kbn/core-saved-objects-base-server-internal';
|
||||
import {
|
||||
getOutdatedDocumentsQuery,
|
||||
type OutdatedDocumentsQueryParams,
|
||||
} from './get_outdated_documents_query';
|
||||
import type { InitState } from './state';
|
||||
import { excludeUnusedTypesQuery } from './core';
|
||||
import { getTempIndexName } from './model/helpers';
|
||||
|
||||
export interface CreateInitialStateParams {
|
||||
export interface CreateInitialStateParams extends OutdatedDocumentsQueryParams {
|
||||
kibanaVersion: string;
|
||||
waitForMigrationCompletion: boolean;
|
||||
mustRelocateDocuments: boolean;
|
||||
indexTypesMap: IndexTypesMap;
|
||||
targetMappings: IndexMapping;
|
||||
preMigrationScript?: string;
|
||||
migrationVersionPerType: SavedObjectsMigrationVersion;
|
||||
indexPrefix: string;
|
||||
migrationsConfig: SavedObjectsMigrationConfigType;
|
||||
typeRegistry: ISavedObjectTypeRegistry;
|
||||
|
@ -46,6 +47,7 @@ export const createInitialState = ({
|
|||
indexTypesMap,
|
||||
targetMappings,
|
||||
preMigrationScript,
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
indexPrefix,
|
||||
migrationsConfig,
|
||||
|
@ -53,37 +55,10 @@ export const createInitialState = ({
|
|||
docLinks,
|
||||
logger,
|
||||
}: CreateInitialStateParams): InitState => {
|
||||
const outdatedDocumentsQuery: QueryDslQueryContainer = {
|
||||
bool: {
|
||||
should: Object.entries(migrationVersionPerType).map(([type, latestVersion]) => ({
|
||||
bool: {
|
||||
must: [
|
||||
{ term: { type } },
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
bool: {
|
||||
must: { exists: { field: 'migrationVersion' } },
|
||||
must_not: { term: { [`migrationVersion.${type}`]: latestVersion } },
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must_not: [
|
||||
{ exists: { field: 'migrationVersion' } },
|
||||
{ term: { typeMigrationVersion: latestVersion } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
},
|
||||
};
|
||||
const outdatedDocumentsQuery = getOutdatedDocumentsQuery({
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
});
|
||||
|
||||
const reindexTargetMappings: IndexMapping = {
|
||||
dynamic: false,
|
||||
|
|
|
@ -18,26 +18,9 @@ import { DocumentMigrator } from './document_migrator';
|
|||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import * as runResilientMigratorModule from './run_resilient_migrator';
|
||||
import { runResilientMigrator } from './run_resilient_migrator';
|
||||
|
||||
jest.mock('./run_resilient_migrator', () => {
|
||||
const actual = jest.requireActual('./run_resilient_migrator');
|
||||
|
||||
return {
|
||||
runResilientMigrator: jest.fn(actual.runResilientMigrator),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('./document_migrator', () => {
|
||||
return {
|
||||
// Create a mock for spying on the constructor
|
||||
DocumentMigrator: jest.fn().mockImplementation((...args) => {
|
||||
const { DocumentMigrator: RealDocMigrator } = jest.requireActual('./document_migrator');
|
||||
return new RealDocMigrator(args[0]);
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const mappingsResponseWithoutIndexTypesMap: estypes.IndicesGetMappingResponse = {
|
||||
'.kibana_8.7.0_001': {
|
||||
mappings: {
|
||||
|
@ -70,8 +53,8 @@ const createRegistry = (types: Array<Partial<SavedObjectsType>>) => {
|
|||
|
||||
describe('KibanaMigrator', () => {
|
||||
beforeEach(() => {
|
||||
(DocumentMigrator as jest.Mock).mockClear();
|
||||
(runResilientMigrator as jest.MockedFunction<typeof runResilientMigrator>).mockClear();
|
||||
jest.restoreAllMocks();
|
||||
jest.spyOn(runResilientMigratorModule, 'runResilientMigrator');
|
||||
});
|
||||
describe('getActiveMappings', () => {
|
||||
it('returns full index mappings w/ core properties', () => {
|
||||
|
@ -110,13 +93,11 @@ describe('KibanaMigrator', () => {
|
|||
it('calls documentMigrator.migrate', () => {
|
||||
const options = mockOptions();
|
||||
const kibanaMigrator = new KibanaMigrator(options);
|
||||
const mockDocumentMigrator = { migrate: jest.fn() };
|
||||
// @ts-expect-error `documentMigrator` is readonly.
|
||||
kibanaMigrator.documentMigrator = mockDocumentMigrator;
|
||||
jest.spyOn(DocumentMigrator.prototype, 'migrate').mockImplementation((doc) => doc);
|
||||
const doc = {} as any;
|
||||
|
||||
expect(() => kibanaMigrator.migrateDocument(doc)).not.toThrowError();
|
||||
expect(mockDocumentMigrator.migrate).toBeCalledTimes(1);
|
||||
expect(DocumentMigrator.prototype.migrate).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ export class KibanaMigrator implements IKibanaMigrator {
|
|||
});
|
||||
|
||||
this.log.debug('Applying registered migrations for the following saved object types:');
|
||||
Object.entries(this.documentMigrator.migrationVersion)
|
||||
Object.entries(this.documentMigrator.getMigrationVersion())
|
||||
.sort(([t1, v1], [t2, v2]) => {
|
||||
return Semver.compare(v1, v2);
|
||||
})
|
||||
|
@ -243,7 +243,13 @@ export class KibanaMigrator implements IKibanaMigrator {
|
|||
migrateDoc: this.documentMigrator.migrateAndConvert,
|
||||
rawDocs,
|
||||
}),
|
||||
migrationVersionPerType: this.documentMigrator.migrationVersion,
|
||||
coreMigrationVersionPerType: this.documentMigrator.getMigrationVersion({
|
||||
includeDeferred: false,
|
||||
migrationType: 'core',
|
||||
}),
|
||||
migrationVersionPerType: this.documentMigrator.getMigrationVersion({
|
||||
includeDeferred: false,
|
||||
}),
|
||||
indexPrefix: indexName,
|
||||
migrationsConfig: this.soMigrationsConfig,
|
||||
typeRegistry: this.typeRegistry,
|
||||
|
|
|
@ -44,6 +44,7 @@ describe('migrationsStateActionMachine', () => {
|
|||
'.kibana_cases': ['typeD', 'typeE'],
|
||||
},
|
||||
targetMappings: { properties: {} },
|
||||
coreMigrationVersionPerType: {},
|
||||
migrationVersionPerType: {},
|
||||
indexPrefix: '.my-so-index',
|
||||
migrationsConfig: {
|
||||
|
|
|
@ -57,6 +57,7 @@ export async function runResilientMigrator({
|
|||
readyToReindex,
|
||||
doneReindexing,
|
||||
transformRawDocs,
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
indexPrefix,
|
||||
migrationsConfig,
|
||||
|
@ -74,6 +75,7 @@ export async function runResilientMigrator({
|
|||
doneReindexing: Defer<any>;
|
||||
logger: Logger;
|
||||
transformRawDocs: TransformRawDocs;
|
||||
coreMigrationVersionPerType: SavedObjectsMigrationVersion;
|
||||
migrationVersionPerType: SavedObjectsMigrationVersion;
|
||||
indexPrefix: string;
|
||||
migrationsConfig: SavedObjectsMigrationConfigType;
|
||||
|
@ -87,6 +89,7 @@ export async function runResilientMigrator({
|
|||
indexTypesMap,
|
||||
targetMappings,
|
||||
preMigrationScript,
|
||||
coreMigrationVersionPerType,
|
||||
migrationVersionPerType,
|
||||
indexPrefix,
|
||||
migrationsConfig,
|
||||
|
|
|
@ -41,8 +41,10 @@ export type {
|
|||
SavedObjectsMappingProperties,
|
||||
} from './src/mapping_definition';
|
||||
export type {
|
||||
SavedObjectMigration,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectMigrationParams,
|
||||
SavedObjectsMigrationLogger,
|
||||
SavedObjectMigrationFn,
|
||||
} from './src/migration';
|
||||
|
|
|
@ -47,6 +47,33 @@ export type SavedObjectMigrationFn<InputAttributes = unknown, MigratedAttributes
|
|||
context: SavedObjectMigrationContext
|
||||
) => SavedObjectUnsanitizedDoc<MigratedAttributes>;
|
||||
|
||||
/**
|
||||
* Saved Objects migration with parameters.
|
||||
* @public
|
||||
*/
|
||||
export interface SavedObjectMigrationParams<
|
||||
InputAttributes = unknown,
|
||||
MigratedAttributes = unknown
|
||||
> {
|
||||
/**
|
||||
* A flag that can defer the migration until either an object is accessed (read) or if there is another non-deferred migration with a higher version.
|
||||
* @default false
|
||||
*/
|
||||
deferred?: false;
|
||||
|
||||
/** {@inheritDoc SavedObjectMigrationFn} */
|
||||
transform: SavedObjectMigrationFn<InputAttributes, MigratedAttributes>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saved Objects migration.
|
||||
* It can be either a {@link SavedObjectMigrationFn | migration function} or a {@link SavedObjectMigrationParams | migration object}.
|
||||
* @public
|
||||
*/
|
||||
export type SavedObjectMigration<InputAttributes = unknown, MigratedAttributes = unknown> =
|
||||
| SavedObjectMigrationFn<InputAttributes, MigratedAttributes>
|
||||
| SavedObjectMigrationParams<InputAttributes, MigratedAttributes>;
|
||||
|
||||
/** @public */
|
||||
export interface SavedObjectsMigrationLogger {
|
||||
debug: (msg: string) => void;
|
||||
|
@ -81,7 +108,7 @@ export interface SavedObjectMigrationContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* A map of {@link SavedObjectMigrationFn | migration functions} to be used for a given type.
|
||||
* A map of {@link SavedObjectMigration | migrations} to be used for a given type.
|
||||
* The map's keys must be valid semver versions, and they cannot exceed the current Kibana version.
|
||||
*
|
||||
* For a given document, only migrations with a higher version number than that of the document will be applied.
|
||||
|
@ -98,5 +125,5 @@ export interface SavedObjectMigrationContext {
|
|||
* @public
|
||||
*/
|
||||
export interface SavedObjectMigrationMap {
|
||||
[version: string]: SavedObjectMigrationFn<any, any>;
|
||||
[version: string]: SavedObjectMigration<any, any>;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { mergeSavedObjectMigrationMaps } from './src/merge_migration_maps';
|
||||
export { mergeSavedObjectMigrations, mergeSavedObjectMigrationMaps } from './src/merge_migrations';
|
||||
export {
|
||||
SavedObjectsUtils,
|
||||
ALL_NAMESPACES_STRING,
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { mergeSavedObjectMigrationMaps } from './merge_migration_maps';
|
||||
|
||||
describe('mergeSavedObjectMigrationMaps', () => {
|
||||
const obj1: SavedObjectMigrationMap = {
|
||||
'7.12.1': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 1 },
|
||||
};
|
||||
},
|
||||
'7.12.2': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 2 },
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const obj2: SavedObjectMigrationMap = {
|
||||
'7.12.0': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter - 2 },
|
||||
};
|
||||
},
|
||||
'7.12.2': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 2 },
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
test('correctly merges two saved object migration maps', () => {
|
||||
const context = { log: { info: jest.fn() } } as unknown as SavedObjectMigrationContext;
|
||||
|
||||
const result = mergeSavedObjectMigrationMaps(obj1, obj2);
|
||||
expect(
|
||||
result['7.12.0']({ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc, context)
|
||||
.attributes.counter
|
||||
).toEqual(3);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(
|
||||
result['7.12.1']({ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc, context)
|
||||
.attributes.counter
|
||||
).toEqual(6);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(
|
||||
result['7.12.2']({ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc, context)
|
||||
.attributes.counter
|
||||
).toEqual(9);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { mergeWith } from 'lodash';
|
||||
import type {
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectMigrationFn,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
|
||||
/**
|
||||
* Merges two saved object migration maps.
|
||||
*
|
||||
* If there is a migration for a given version on only one of the maps,
|
||||
* that migration function will be used:
|
||||
*
|
||||
* mergeSavedObjectMigrationMaps({ '1.2.3': f }, { '4.5.6': g }) -> { '1.2.3': f, '4.5.6': g }
|
||||
*
|
||||
* If there is a migration for a given version on both maps, the migrations will be composed:
|
||||
*
|
||||
* mergeSavedObjectMigrationMaps({ '1.2.3': f }, { '1.2.3': g }) -> { '1.2.3': (doc, context) => f(g(doc, context), context) }
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @param map1 - The first map to merge
|
||||
* @param map2 - The second map to merge
|
||||
* @returns The merged map {@link SavedObjectMigrationMap}
|
||||
*/
|
||||
export const mergeSavedObjectMigrationMaps = (
|
||||
map1: SavedObjectMigrationMap,
|
||||
map2: SavedObjectMigrationMap
|
||||
): SavedObjectMigrationMap => {
|
||||
const customizer = (objValue: SavedObjectMigrationFn, srcValue: SavedObjectMigrationFn) => {
|
||||
if (!srcValue || !objValue) {
|
||||
return srcValue || objValue;
|
||||
}
|
||||
return (state: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) =>
|
||||
objValue(srcValue(state, context), context);
|
||||
};
|
||||
|
||||
return mergeWith({ ...map1 }, map2, customizer);
|
||||
};
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectMigrationFn,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { mergeSavedObjectMigrations, mergeSavedObjectMigrationMaps } from './merge_migrations';
|
||||
|
||||
describe('mergeSavedObjectMigrations', () => {
|
||||
test('merges migration parameters with a migration function', () => {
|
||||
// @ts-expect-error
|
||||
expect(mergeSavedObjectMigrations({ deferred: true, transform: jest.fn() }, jest.fn())).toEqual(
|
||||
{
|
||||
deferred: false,
|
||||
transform: expect.any(Function),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('returns a function on merging two functions', () => {
|
||||
expect(mergeSavedObjectMigrations(jest.fn(), jest.fn())).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
test('merges two deferred migrations', () => {
|
||||
expect(
|
||||
mergeSavedObjectMigrations(
|
||||
// @ts-expect-error
|
||||
{ deferred: true, transform: jest.fn() },
|
||||
{ deferred: true, transform: jest.fn() }
|
||||
)
|
||||
).toEqual({
|
||||
deferred: true,
|
||||
transform: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
test('merges two non-deferred migrations', () => {
|
||||
expect(
|
||||
mergeSavedObjectMigrations(
|
||||
{ deferred: false, transform: jest.fn() },
|
||||
{ deferred: false, transform: jest.fn() }
|
||||
)
|
||||
).toEqual({
|
||||
deferred: false,
|
||||
transform: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
test('merges deferred and non-deferred migrations', () => {
|
||||
expect(
|
||||
mergeSavedObjectMigrations(
|
||||
// @ts-expect-error
|
||||
{ deferred: true, transform: jest.fn() },
|
||||
{ deferred: false, transform: jest.fn() }
|
||||
)
|
||||
).toEqual({
|
||||
deferred: false,
|
||||
transform: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeSavedObjectMigrationMaps', () => {
|
||||
test('correctly merges two saved object migration maps', () => {
|
||||
const obj1: SavedObjectMigrationMap = {
|
||||
'7.12.1': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 1 },
|
||||
};
|
||||
},
|
||||
'7.12.2': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 2 },
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const obj2: SavedObjectMigrationMap = {
|
||||
'7.12.0': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter - 2 },
|
||||
};
|
||||
},
|
||||
'7.12.2': (doc, context) => {
|
||||
context.log.info('');
|
||||
return {
|
||||
...doc,
|
||||
attributes: { ...doc.attributes, counter: doc.attributes.counter + 2 },
|
||||
};
|
||||
},
|
||||
};
|
||||
const context = { log: { info: jest.fn() } } as unknown as SavedObjectMigrationContext;
|
||||
|
||||
const result = mergeSavedObjectMigrationMaps(obj1, obj2);
|
||||
expect(
|
||||
(result['7.12.0'] as SavedObjectMigrationFn)(
|
||||
{ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc,
|
||||
context
|
||||
)
|
||||
).toHaveProperty('attributes.counter', 3);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(
|
||||
(result['7.12.1'] as SavedObjectMigrationFn)(
|
||||
{ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc,
|
||||
context
|
||||
)
|
||||
).toHaveProperty('attributes.counter', 6);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(
|
||||
(result['7.12.2'] as SavedObjectMigrationFn)(
|
||||
{ attributes: { counter: 5 } } as SavedObjectUnsanitizedDoc,
|
||||
context
|
||||
)
|
||||
).toHaveProperty('attributes.counter', 9);
|
||||
expect(context.log.info).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ary, isFunction, mergeWith } from 'lodash';
|
||||
import type {
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectMigration,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
|
||||
/**
|
||||
* Composes two migrations into a single migration.
|
||||
* ```
|
||||
* mergeSavedObjectMigrations(outer, inner) -> (doc, context) => outer(inner(doc, context), context) }
|
||||
* ```
|
||||
*
|
||||
* If at least one of the migrations is not deferred, the composed migration will not be deferred.
|
||||
*
|
||||
* @public
|
||||
* @param outer Wrapping migration.
|
||||
* @param inner Wrapped migration.
|
||||
* @param rest Additional wrapped migrations to compose.
|
||||
* @returns The composed migration can be either a function or an object depending on the input migrations.
|
||||
*/
|
||||
export function mergeSavedObjectMigrations(
|
||||
outer: SavedObjectMigration,
|
||||
inner: SavedObjectMigration,
|
||||
...rest: SavedObjectMigration[]
|
||||
): SavedObjectMigration {
|
||||
if (rest.length) {
|
||||
return mergeSavedObjectMigrations(
|
||||
mergeSavedObjectMigrations(outer, inner),
|
||||
...(rest as [SavedObjectMigration, ...SavedObjectMigration[]])
|
||||
);
|
||||
}
|
||||
if (!inner || !outer) {
|
||||
return inner || outer;
|
||||
}
|
||||
|
||||
const innerMigration = isFunction(inner) ? { transform: inner } : inner;
|
||||
const outerMigration = isFunction(outer) ? { transform: outer } : outer;
|
||||
const merged = {
|
||||
deferred: !!(innerMigration.deferred && outerMigration.deferred),
|
||||
transform: (state: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) =>
|
||||
outerMigration.transform(innerMigration.transform(state, context), context),
|
||||
};
|
||||
|
||||
if (isFunction(inner) && isFunction(outer)) {
|
||||
return merged.transform;
|
||||
}
|
||||
|
||||
return merged as SavedObjectMigration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two saved object migration maps.
|
||||
*
|
||||
* If there is a migration for a given version on only one of the maps,
|
||||
* that migration function will be used:
|
||||
*
|
||||
* mergeSavedObjectMigrationMaps({ '1.2.3': f }, { '4.5.6': g }) -> { '1.2.3': f, '4.5.6': g }
|
||||
*
|
||||
* If there is a migration for a given version on both maps, the migrations will be composed:
|
||||
*
|
||||
* mergeSavedObjectMigrationMaps({ '1.2.3': f }, { '1.2.3': g }) -> { '1.2.3': (doc, context) => f(g(doc, context), context) }
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @param map1 - The first map to merge
|
||||
* @param map2 - The second map to merge
|
||||
* @returns The merged map {@link SavedObjectMigrationMap}
|
||||
*/
|
||||
export function mergeSavedObjectMigrationMaps(
|
||||
map1: SavedObjectMigrationMap,
|
||||
map2: SavedObjectMigrationMap
|
||||
): SavedObjectMigrationMap {
|
||||
return mergeWith({ ...map1 }, map2, ary(mergeSavedObjectMigrations, 2));
|
||||
}
|
|
@ -107,4 +107,18 @@ describe('SavedObjectsUtils', () => {
|
|||
expect(mockUuidv5).toHaveBeenCalledWith('namespace:type:oldId', 'DNSUUID');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getMigrationFunction', () => {
|
||||
it('should return the migration function when it is a function', () => {
|
||||
const migration = jest.fn();
|
||||
|
||||
expect(SavedObjectsUtils.getMigrationFunction(migration)).toBe(migration);
|
||||
});
|
||||
|
||||
it('should return the migration function when it is a migration object', () => {
|
||||
const migration = { transform: jest.fn() };
|
||||
|
||||
expect(SavedObjectsUtils.getMigrationFunction(migration)).toBe(migration.transform);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isFunction } from 'lodash';
|
||||
import { v1 as uuidv1, v5 as uuidv5 } from 'uuid';
|
||||
import type {
|
||||
SavedObjectsFindOptions,
|
||||
SavedObjectsFindResponse,
|
||||
} from '@kbn/core-saved-objects-api-server';
|
||||
import type { SavedObjectMigration, SavedObjectMigrationFn } from '@kbn/core-saved-objects-server';
|
||||
|
||||
export const DEFAULT_NAMESPACE_STRING = 'default';
|
||||
export const ALL_NAMESPACES_STRING = '*';
|
||||
|
@ -95,4 +97,15 @@ export class SavedObjectsUtils {
|
|||
}
|
||||
return uuidv5(`${namespace}:${type}:${id}`, uuidv5.DNS); // The uuidv5 namespace constant (uuidv5.DNS) is arbitrary.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transform function from a migration object.
|
||||
* @param migration Migration object or a migration function.
|
||||
* @returns A migration function.
|
||||
*/
|
||||
public static getMigrationFunction<InputAttributes, MigratedAttributes>(
|
||||
migration: SavedObjectMigration<InputAttributes, MigratedAttributes>
|
||||
): SavedObjectMigrationFn<InputAttributes, MigratedAttributes> {
|
||||
return isFunction(migration) ? migration : migration.transform;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server';
|
||||
import type {
|
||||
SavedObjectsRawDocSource,
|
||||
SavedObjectsType,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { delay } from '../test_utils';
|
||||
|
||||
import '../jest_matchers';
|
||||
import {
|
||||
clearLog,
|
||||
defaultKibanaIndex,
|
||||
startElasticsearch,
|
||||
KibanaMigratorTestKit,
|
||||
getKibanaMigratorTestKit,
|
||||
} from '../kibana_migrator_test_kit';
|
||||
|
||||
describe('deferred migrations', () => {
|
||||
let client: KibanaMigratorTestKit['client'];
|
||||
let runMigrations: KibanaMigratorTestKit['runMigrations'];
|
||||
let savedObjectsRepository: KibanaMigratorTestKit['savedObjectsRepository'];
|
||||
let server: TestElasticsearchUtils['es'];
|
||||
let type: SavedObjectsType;
|
||||
|
||||
beforeAll(async () => {
|
||||
server = await startElasticsearch();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server?.stop();
|
||||
await delay(10);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const noop = (doc: SavedObjectUnsanitizedDoc) => doc;
|
||||
|
||||
type = {
|
||||
name: 'some-type',
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
mappings: {
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
},
|
||||
},
|
||||
migrations: {
|
||||
'1.0.0': jest.fn(noop),
|
||||
'2.0.0': jest.fn(noop),
|
||||
'3.0.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(noop),
|
||||
},
|
||||
'4.0.0': jest.fn(noop),
|
||||
'5.0.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(noop),
|
||||
},
|
||||
'6.0.0': {
|
||||
// @ts-expect-error
|
||||
deferred: true,
|
||||
transform: jest.fn(noop),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
({ client, runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({
|
||||
kibanaIndex: defaultKibanaIndex,
|
||||
types: [type],
|
||||
}));
|
||||
await clearLog();
|
||||
});
|
||||
|
||||
describe.each`
|
||||
source | expected
|
||||
${'1.0.0'} | ${'6.0.0'}
|
||||
${'2.0.0'} | ${'6.0.0'}
|
||||
${'3.0.0'} | ${'6.0.0'}
|
||||
${'4.0.0'} | ${'4.0.0'}
|
||||
${'5.0.0'} | ${'5.0.0'}
|
||||
${'6.0.0'} | ${'6.0.0'}
|
||||
`("when source document version is '$source'", ({ source, expected }) => {
|
||||
let id: string;
|
||||
let latestVersion: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
id = `${type.name}:test-document-${source}`;
|
||||
latestVersion = Object.keys(type.migrations!).sort().pop()!;
|
||||
|
||||
await client.create<SavedObjectsRawDocSource>({
|
||||
id: `${type.name}:${id}`,
|
||||
index: defaultKibanaIndex,
|
||||
refresh: 'wait_for',
|
||||
document: {
|
||||
type: type.name,
|
||||
references: [],
|
||||
typeMigrationVersion: source,
|
||||
coreMigrationVersion: '7.0.0',
|
||||
},
|
||||
});
|
||||
await runMigrations();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await client.delete({
|
||||
id: `${type.name}:${id}`,
|
||||
index: defaultKibanaIndex,
|
||||
refresh: 'wait_for',
|
||||
});
|
||||
});
|
||||
|
||||
it(`should migrate to '${expected}'`, async () => {
|
||||
await expect(
|
||||
client.get({
|
||||
id: `${type.name}:${id}`,
|
||||
index: defaultKibanaIndex,
|
||||
})
|
||||
).resolves.toHaveProperty('_source.typeMigrationVersion', expected);
|
||||
});
|
||||
|
||||
it('should return the latest version via `repository.get`', async () => {
|
||||
await expect(savedObjectsRepository.get(type.name, id)).resolves.toHaveProperty(
|
||||
'typeMigrationVersion',
|
||||
latestVersion
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the latest version via `repository.bulkGet`', async () => {
|
||||
await expect(
|
||||
savedObjectsRepository.bulkGet([{ id, type: type.name }])
|
||||
).resolves.toHaveProperty('saved_objects.0.typeMigrationVersion', latestVersion);
|
||||
});
|
||||
|
||||
it('should return the latest version via `repository.resolve`', async () => {
|
||||
await expect(savedObjectsRepository.resolve(type.name, id)).resolves.toHaveProperty(
|
||||
'saved_object.typeMigrationVersion',
|
||||
latestVersion
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the latest version via `repository.bulkResolve`', async () => {
|
||||
await expect(
|
||||
savedObjectsRepository.bulkResolve([{ id, type: type.name }])
|
||||
).resolves.toHaveProperty(
|
||||
'resolved_objects.0.saved_object.typeMigrationVersion',
|
||||
latestVersion
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the latest version via `repository.find`', async () => {
|
||||
await expect(savedObjectsRepository.find({ type: type.name })).resolves.toHaveProperty(
|
||||
'saved_objects.0.typeMigrationVersion',
|
||||
latestVersion
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -164,9 +164,11 @@ export const getKibanaMigratorTestKit = async ({
|
|||
}
|
||||
hasRun = true;
|
||||
migrator.prepareMigrations();
|
||||
const migrationResults = await migrator.runMigrations();
|
||||
await loggingSystem.stop();
|
||||
return migrationResults;
|
||||
try {
|
||||
return await migrator.runMigrations();
|
||||
} finally {
|
||||
await loggingSystem.stop();
|
||||
}
|
||||
};
|
||||
|
||||
const savedObjectsRepository = SavedObjectsRepository.createRepository(
|
||||
|
|
|
@ -97,7 +97,7 @@ describe('ZDT upgrades - basic downgrade', () => {
|
|||
it('migrates the documents', async () => {
|
||||
await createBaseline();
|
||||
|
||||
const { runMigrations, client, savedObjectsRepository } = await getKibanaMigratorTestKit({
|
||||
const { runMigrations, client } = await getKibanaMigratorTestKit({
|
||||
...getBaseMigratorParams(),
|
||||
logFilePath,
|
||||
types: [typeV1],
|
||||
|
@ -129,10 +129,9 @@ describe('ZDT upgrades - basic downgrade', () => {
|
|||
sample_type: '10.2.0',
|
||||
});
|
||||
|
||||
const { saved_objects: sampleDocs } = await savedObjectsRepository.find({
|
||||
type: 'sample_type',
|
||||
});
|
||||
expect(sampleDocs).toHaveLength(5);
|
||||
await expect(
|
||||
client.count({ index: '.kibana_1', query: { term: { type: 'sample_type' } } })
|
||||
).resolves.toHaveProperty('count', 5);
|
||||
|
||||
const records = await parseLogFile(logFilePath);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('ZDT upgrades - encountering conversion failures', () => {
|
|||
|
||||
describe('when discardCorruptObjects is false', () => {
|
||||
it('fails the migration with an explicit message and keep the documents', async () => {
|
||||
const { runMigrations, savedObjectsRepository } = await prepareScenario({
|
||||
const { client, runMigrations } = await prepareScenario({
|
||||
discardCorruptObjects: false,
|
||||
});
|
||||
|
||||
|
@ -84,15 +84,13 @@ describe('ZDT upgrades - encountering conversion failures', () => {
|
|||
const records = await parseLogFile(logFilePath);
|
||||
expect(records).toContainLogEntry('OUTDATED_DOCUMENTS_SEARCH_READ -> FATAL');
|
||||
|
||||
const { saved_objects: sampleADocs } = await savedObjectsRepository.find({
|
||||
type: 'sample_a',
|
||||
});
|
||||
const { saved_objects: sampleBDocs } = await savedObjectsRepository.find({
|
||||
type: 'sample_b',
|
||||
});
|
||||
|
||||
expect(sampleADocs).toHaveLength(5);
|
||||
expect(sampleBDocs).toHaveLength(5);
|
||||
const { kibanaIndex: index } = getBaseMigratorParams();
|
||||
await expect(
|
||||
client.count({ index, query: { term: { type: 'sample_a' } } })
|
||||
).resolves.toHaveProperty('count', 5);
|
||||
await expect(
|
||||
client.count({ index, query: { term: { type: 'sample_b' } } })
|
||||
).resolves.toHaveProperty('count', 5);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import { SerializableRecord } from '@kbn/utility-types';
|
|||
import { savedObjectsServiceMock } from '@kbn/core/server/mocks';
|
||||
import { createEmbeddableSetupMock } from '@kbn/embeddable-plugin/server/mocks';
|
||||
import { SavedObjectReference, SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
import { createExtract, createInject } from '../../../common';
|
||||
import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common';
|
||||
|
@ -47,7 +48,7 @@ const contextMock = savedObjectsServiceMock.createMigrationContext();
|
|||
|
||||
describe('dashboard', () => {
|
||||
describe('7.0.0', () => {
|
||||
const migration = migrations['7.0.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(migrations['7.0.0']);
|
||||
|
||||
test('skips error on empty object', () => {
|
||||
expect(migration({} as SavedObjectUnsanitizedDoc, contextMock)).toMatchInlineSnapshot(`
|
||||
|
@ -472,7 +473,7 @@ describe('dashboard', () => {
|
|||
});
|
||||
|
||||
describe('7.10.0 - hidden panel titles', () => {
|
||||
const migration = migrations['7.17.3'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(migrations['7.17.3']);
|
||||
const doc: DashboardDoc730ToLatest = {
|
||||
attributes: {
|
||||
description: '',
|
||||
|
@ -579,7 +580,7 @@ describe('dashboard', () => {
|
|||
});
|
||||
|
||||
describe('7.11.0 - embeddable persistable state extraction', () => {
|
||||
const migration = migrations['7.11.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(migrations['7.11.0']);
|
||||
const doc: DashboardDoc730ToLatest = {
|
||||
attributes: {
|
||||
description: '',
|
||||
|
@ -703,7 +704,10 @@ describe('dashboard', () => {
|
|||
embeddable: newEmbeddableSetupMock,
|
||||
});
|
||||
expect(migrationsList['7.13.0']).toBeDefined();
|
||||
const migratedDoc = migrationsList['7.13.0'](originalDoc, contextMock);
|
||||
const migratedDoc = SavedObjectsUtils.getMigrationFunction(migrationsList['7.13.0'])(
|
||||
originalDoc,
|
||||
contextMock
|
||||
);
|
||||
expect(migratedDoc.attributes.panelsJSON).toMatchInlineSnapshot(
|
||||
`"[{\\"version\\":\\"7.9.3\\",\\"gridData\\":{\\"x\\":0,\\"y\\":0,\\"w\\":24,\\"h\\":15,\\"i\\":\\"0\\"},\\"panelIndex\\":\\"0\\",\\"embeddableConfig\\":{}},{\\"version\\":\\"7.13.0\\",\\"gridData\\":{\\"x\\":24,\\"y\\":0,\\"w\\":24,\\"h\\":15,\\"i\\":\\"1\\"},\\"panelIndex\\":\\"1\\",\\"embeddableConfig\\":{\\"attributes\\":{\\"byValueThing\\":\\"ThisIsByValue\\"},\\"superCoolKey\\":\\"ONLY 4 BY VALUE EMBEDDABLES THANK YOU VERY MUCH\\"}}]"`
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { savedObjectsServiceMock } from '@kbn/core/server/mocks';
|
||||
import { createEmbeddableSetupMock } from '@kbn/embeddable-plugin/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
import {
|
||||
DashboardDocPre700,
|
||||
|
@ -87,8 +88,8 @@ test('dashboard migration 7.3.0 migrates filters to query on search source when
|
|||
},
|
||||
};
|
||||
|
||||
const doc700 = migrations['7.0.0'](doc, mockContext);
|
||||
const newDoc = migrations['7.3.0'](doc700, mockContext);
|
||||
const doc700 = SavedObjectsUtils.getMigrationFunction(migrations['7.0.0'])(doc, mockContext);
|
||||
const newDoc = SavedObjectsUtils.getMigrationFunction(migrations['7.3.0'])(doc700, mockContext);
|
||||
|
||||
const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON);
|
||||
expect(parsedSearchSource.filter.length).toBe(0);
|
||||
|
@ -119,8 +120,8 @@ test('dashboard migration works when panelsJSON is missing panelIndex', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const doc700 = migrations['7.0.0'](doc, mockContext);
|
||||
const newDoc = migrations['7.3.0'](doc700, mockContext);
|
||||
const doc700 = SavedObjectsUtils.getMigrationFunction(migrations['7.0.0'])(doc, mockContext);
|
||||
const newDoc = SavedObjectsUtils.getMigrationFunction(migrations['7.3.0'])(doc700, mockContext);
|
||||
|
||||
const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON);
|
||||
expect(parsedSearchSource.filter.length).toBe(0);
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"@kbn/saved-objects-management-plugin",
|
||||
"@kbn/shared-ux-button-toolbar",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import { SavedObject } from '@kbn/core/types';
|
||||
import { SEARCH_SESSION_TYPE, SearchSessionStatus, SearchStatus } from '../../../common';
|
||||
import { SavedObjectMigrationContext } from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
describe('7.12.0 -> 7.13.0', () => {
|
||||
const mockCompletedSessionSavedObject: SavedObject<SearchSessionSavedObjectAttributesPre$7$13$0> =
|
||||
|
@ -66,7 +67,9 @@ describe('7.12.0 -> 7.13.0', () => {
|
|||
references: [],
|
||||
};
|
||||
|
||||
const migration = searchSessionSavedObjectMigrations['7.13.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(
|
||||
searchSessionSavedObjectMigrations['7.13.0']
|
||||
);
|
||||
test('"completed" is populated from "touched" for completed session', () => {
|
||||
const migratedCompletedSession = migration(
|
||||
mockCompletedSessionSavedObject,
|
||||
|
@ -138,7 +141,9 @@ describe('7.13.0 -> 7.14.0', () => {
|
|||
references: [],
|
||||
};
|
||||
|
||||
const migration = searchSessionSavedObjectMigrations['7.14.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(
|
||||
searchSessionSavedObjectMigrations['7.14.0']
|
||||
);
|
||||
test('version is populated', () => {
|
||||
const migratedSession = migration(mockSessionSavedObject, {} as SavedObjectMigrationContext);
|
||||
|
||||
|
@ -169,7 +174,9 @@ describe('7.13.0 -> 7.14.0', () => {
|
|||
});
|
||||
|
||||
describe('7.14.0 -> 8.0.0', () => {
|
||||
const migration = searchSessionSavedObjectMigrations['8.0.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(
|
||||
searchSessionSavedObjectMigrations['8.0.0']
|
||||
);
|
||||
|
||||
test('Discover app URL generator migrates to locator', () => {
|
||||
const mockSessionSavedObject: SavedObject<SearchSessionSavedObjectAttributesPre$8$0$0> = {
|
||||
|
@ -359,7 +366,9 @@ describe('7.14.0 -> 8.0.0', () => {
|
|||
});
|
||||
|
||||
describe('8.0.0 -> 8.6.0', () => {
|
||||
const migration = searchSessionSavedObjectMigrations['8.6.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(
|
||||
searchSessionSavedObjectMigrations['8.6.0']
|
||||
);
|
||||
|
||||
const mockSessionSavedObject: SavedObject<SearchSessionSavedObjectAttributesPre$8$6$0> = {
|
||||
id: 'id',
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"@kbn/config-schema",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { getAllMigrations, searchMigrations } from './search_migrations';
|
||||
|
||||
const savedObjectMigrationContext = null as unknown as SavedObjectMigrationContext;
|
||||
|
@ -369,7 +370,12 @@ Object {
|
|||
[versionToTest]: (state) => ({ ...state, migrated: true }),
|
||||
});
|
||||
|
||||
expect(migrations[versionToTest](savedSearch, {} as SavedObjectMigrationContext)).toEqual({
|
||||
expect(
|
||||
SavedObjectsUtils.getMigrationFunction(migrations[versionToTest])(
|
||||
savedSearch,
|
||||
{} as SavedObjectMigrationContext
|
||||
)
|
||||
).toEqual({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: JSON.stringify({
|
||||
|
@ -395,7 +401,12 @@ Object {
|
|||
[versionToTest]: (state) => ({ ...state, migrated: true }),
|
||||
});
|
||||
|
||||
expect(migrations[versionToTest](savedSearch, {} as SavedObjectMigrationContext)).toEqual({
|
||||
expect(
|
||||
SavedObjectsUtils.getMigrationFunction(migrations[versionToTest])(
|
||||
savedSearch,
|
||||
{} as SavedObjectMigrationContext
|
||||
)
|
||||
).toEqual({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: '5',
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"@kbn/i18n",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -12,6 +12,7 @@ import type {
|
|||
SavedObjectsType,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { ServerShortUrlClientFactory } from '..';
|
||||
import { UrlService, LocatorDefinition } from '../../../common/url_service';
|
||||
import { LegacyShortUrlLocatorDefinition } from '../../../common/url_service/locators/legacy_short_url_locator';
|
||||
|
@ -101,9 +102,9 @@ describe('migrations', () => {
|
|||
|
||||
service.locators.create(new FooLocatorDefinition());
|
||||
|
||||
const migrationFunction = (type.migrations as () => SavedObjectMigrationMap)()['8.0.0'];
|
||||
|
||||
expect(typeof migrationFunction).toBe('function');
|
||||
const migrationFunction = SavedObjectsUtils.getMigrationFunction(
|
||||
(type.migrations as () => SavedObjectMigrationMap)()['8.0.0']
|
||||
);
|
||||
|
||||
const doc1: SavedObjectUnsanitizedDoc<ShortUrlSavedObjectAttributes> = {
|
||||
id: 'foo',
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"@kbn/i18n-react",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/core-custom-branding-browser",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
SavedObjectMigrationFn,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
const savedObjectMigrationContext = null as unknown as SavedObjectMigrationContext;
|
||||
|
||||
|
@ -64,7 +65,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('6.7.2', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['6.7.2'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['6.7.2'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -198,7 +199,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.0.0', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.0.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.0.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -783,7 +784,7 @@ describe('migration visualization', () => {
|
|||
describe('7.2.0', () => {
|
||||
describe('date histogram custom interval removal', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.2.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.2.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -935,7 +936,7 @@ describe('migration visualization', () => {
|
|||
} as unknown as SavedObjectMigrationContext;
|
||||
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.3.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.3.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
logger
|
||||
);
|
||||
|
@ -1126,7 +1127,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.3.0 tsvb', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.3.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.3.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1222,7 +1223,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.3.1', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.3.1'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.3.1'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1266,7 +1267,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.4.2 tsvb split_filters migration', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.4.2'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.4.2'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1399,7 +1400,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.7.0 tsvb opperator typo migration', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.7.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.7.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1506,7 +1507,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.9.3', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.9.3'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.9.3'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1518,7 +1519,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.8.0 tsvb split_color_mode', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.8.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.8.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1576,7 +1577,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.10.0 tsvb filter_ratio migration', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.10.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.10.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1610,7 +1611,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.10.0 remove tsvb search source', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.10.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.10.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1650,7 +1651,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.11.0 Data table vis - enable toolbar', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.11.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.11.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1672,7 +1673,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.12.0 update vislib visualization defaults', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.12.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.12.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1817,7 +1818,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.12.0 update "schema" in aggregations', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.12.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.12.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1958,7 +1959,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.13.0 tsvb hide Last value indicator by default', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.13.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.13.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -1990,7 +1991,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.13.0 tsvb - remove default_index_pattern and default_timefield from Model', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.13.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.13.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2014,13 +2015,13 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.13.0 and 7.13.1 tsvb migrations can run twice', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.13.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.13.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
||||
const migrateAgain = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.13.1'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.13.1'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2054,7 +2055,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.14.0 tsvb - add empty value rule to savedObjects with less and greater then zero rules', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2151,7 +2152,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.14.0 tsvb - add drop last bucket into TSVB model', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2232,7 +2233,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.14.0 update pie visualization defaults', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2280,7 +2281,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.14.0 replaceIndexPatternReference', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2310,7 +2311,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.14.0 update tagcloud defaults', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2351,7 +2352,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('8.0.0 removeMarkdownLessFromTSVB', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['8.0.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['8.0.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2383,13 +2384,13 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('7.17.0 tsvb - add drop last bucket into TSVB model', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.14.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.14.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
||||
const migrateAgain = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['7.17.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['7.17.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2457,7 +2458,10 @@ describe('migration visualization', () => {
|
|||
});
|
||||
|
||||
expect(
|
||||
visMigrations[versionToTest](visualizationDoc, {} as SavedObjectMigrationContext)
|
||||
SavedObjectsUtils.getMigrationFunction(visMigrations[versionToTest])(
|
||||
visualizationDoc,
|
||||
{} as SavedObjectMigrationContext
|
||||
)
|
||||
).toEqual({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
|
@ -2485,7 +2489,10 @@ describe('migration visualization', () => {
|
|||
});
|
||||
|
||||
expect(
|
||||
visMigrations[versionToTest](visualizationDoc, {} as SavedObjectMigrationContext)
|
||||
SavedObjectsUtils.getMigrationFunction(visMigrations[versionToTest])(
|
||||
visualizationDoc,
|
||||
{} as SavedObjectMigrationContext
|
||||
)
|
||||
).toEqual({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
|
@ -2543,7 +2550,7 @@ describe('migration visualization', () => {
|
|||
},
|
||||
});
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['8.1.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['8.1.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2580,7 +2587,7 @@ describe('migration visualization', () => {
|
|||
},
|
||||
});
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['8.3.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['8.3.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
@ -2626,7 +2633,7 @@ describe('migration visualization', () => {
|
|||
|
||||
describe('8.5.0 tsvb - remove exclamation circle icon', () => {
|
||||
const migrate = (doc: any) =>
|
||||
visualizationSavedObjectTypeMigrations['8.5.0'](
|
||||
SavedObjectsUtils.getMigrationFunction(visualizationSavedObjectTypeMigrations['8.5.0'])(
|
||||
doc as Parameters<SavedObjectMigrationFn>[0],
|
||||
savedObjectMigrationContext
|
||||
);
|
||||
|
|
|
@ -56,7 +56,8 @@
|
|||
"@kbn/content-management-plugin",
|
||||
"@kbn/core-saved-objects-api-server",
|
||||
"@kbn/object-versioning",
|
||||
"@kbn/core-saved-objects-server"
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core-saved-objects-utils-server"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const es = getService('es');
|
||||
|
||||
const BULK_REQUESTS = [
|
||||
{
|
||||
|
@ -265,5 +267,31 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(resp.body.saved_objects[2].managed).not.to.be.ok();
|
||||
expect(resp.body.saved_objects[3].managed).to.be.ok();
|
||||
}));
|
||||
|
||||
it('should migrate saved object before returning', async () => {
|
||||
await es.update({
|
||||
index: MAIN_SAVED_OBJECT_INDEX,
|
||||
id: 'config:7.0.0-alpha1',
|
||||
doc: {
|
||||
coreMigrationVersion: '7.0.0',
|
||||
typeMigrationVersion: '7.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.post(`/api/saved_objects/_bulk_get`)
|
||||
.send([
|
||||
{
|
||||
type: 'config',
|
||||
id: '7.0.0-alpha1',
|
||||
},
|
||||
])
|
||||
.expect(200);
|
||||
|
||||
expect(body.saved_objects[0].coreMigrationVersion).to.be.ok();
|
||||
expect(body.saved_objects[0].coreMigrationVersion).not.to.be('7.0.0');
|
||||
expect(body.saved_objects[0].typeMigrationVersion).to.be.ok();
|
||||
expect(body.saved_objects[0].typeMigrationVersion).not.to.be('7.0.0');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
|
||||
import expect from '@kbn/expect';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
@ -13,6 +14,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const es = getService('es');
|
||||
const SPACE_ID = 'ftr-so-find';
|
||||
const UUID_PATTERN = new RegExp(
|
||||
/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
|
||||
|
@ -52,6 +54,27 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(resp.body.saved_objects[0].typeMigrationVersion).to.be.ok();
|
||||
}));
|
||||
|
||||
it('should migrate saved object before returning', async () => {
|
||||
await es.update({
|
||||
index: MAIN_SAVED_OBJECT_INDEX,
|
||||
id: `${SPACE_ID}:config:7.0.0-alpha1`,
|
||||
doc: {
|
||||
coreMigrationVersion: '7.0.0',
|
||||
typeMigrationVersion: '7.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.get(`/s/${SPACE_ID}/api/saved_objects/_find?type=config`)
|
||||
.expect(200);
|
||||
|
||||
expect(body.saved_objects.map((so: { id: string }) => so.id)).to.eql(['7.0.0-alpha1']);
|
||||
expect(body.saved_objects[0].coreMigrationVersion).to.be.ok();
|
||||
expect(body.saved_objects[0].coreMigrationVersion).not.to.be('7.0.0');
|
||||
expect(body.saved_objects[0].typeMigrationVersion).to.be.ok();
|
||||
expect(body.saved_objects[0].typeMigrationVersion).not.to.be('7.0.0');
|
||||
});
|
||||
|
||||
describe('unknown type', () => {
|
||||
it('should return 200 with empty response', async () =>
|
||||
await supertest
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
|
@ -113,6 +115,24 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
it('should migrate saved object before returning', async () => {
|
||||
await es.update({
|
||||
index: MAIN_SAVED_OBJECT_INDEX,
|
||||
id: 'config:7.0.0-alpha1',
|
||||
doc: {
|
||||
coreMigrationVersion: '7.0.0',
|
||||
typeMigrationVersion: '7.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
const { body } = await supertest.get(`/api/saved_objects/config/7.0.0-alpha1`).expect(200);
|
||||
|
||||
expect(body.coreMigrationVersion).to.be.ok();
|
||||
expect(body.coreMigrationVersion).not.to.be('7.0.0');
|
||||
expect(body.typeMigrationVersion).to.be.ok();
|
||||
expect(body.typeMigrationVersion).not.to.be('7.0.0');
|
||||
});
|
||||
|
||||
describe('doc does not exist', () => {
|
||||
it('should return same generic error as when index does not exist', async () =>
|
||||
await supertest
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
|
||||
import expect from '@kbn/expect';
|
||||
import type { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
|
@ -70,6 +72,26 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(resp.body.saved_object.managed).to.not.be.ok();
|
||||
}));
|
||||
|
||||
it('should migrate saved object before returning', async () => {
|
||||
await es.update({
|
||||
index: MAIN_SAVED_OBJECT_INDEX,
|
||||
id: 'config:7.0.0-alpha1',
|
||||
doc: {
|
||||
coreMigrationVersion: '7.0.0',
|
||||
typeMigrationVersion: '7.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
const { body } = await supertest
|
||||
.get(`/api/saved_objects/resolve/config/7.0.0-alpha1`)
|
||||
.expect(200);
|
||||
|
||||
expect(body.saved_object.coreMigrationVersion).to.be.ok();
|
||||
expect(body.saved_object.coreMigrationVersion).not.to.be('7.0.0');
|
||||
expect(body.saved_object.typeMigrationVersion).to.be.ok();
|
||||
expect(body.saved_object.typeMigrationVersion).not.to.be('7.0.0');
|
||||
});
|
||||
|
||||
describe('doc does not exist', () => {
|
||||
it('should return same generic error as when index does not exist', async () =>
|
||||
await supertest
|
||||
|
|
|
@ -14,6 +14,7 @@ import { ActionTaskParams } from '../types';
|
|||
import { SavedObjectReference, SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
|
||||
import { migrationMocks } from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
const context = migrationMocks.createContext();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
|
@ -38,10 +39,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.16.0', () => {
|
||||
test('adds actionId to references array if actionId is not preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData();
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
|
@ -57,10 +57,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not add actionId to references array if actionId is preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({ actionId: 'my-slack1' });
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
|
@ -70,10 +69,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('handles empty relatedSavedObjects array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({ relatedSavedObjects: [] });
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
|
@ -93,10 +91,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('adds actionId and relatedSavedObjects to references array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
|
@ -137,10 +134,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('only adds relatedSavedObjects to references array if action is preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({
|
||||
actionId: 'my-slack1',
|
||||
relatedSavedObjects: [
|
||||
|
@ -177,10 +173,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('adds actionId and multiple relatedSavedObjects to references array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
|
@ -236,10 +231,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not overwrite existing references', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData(
|
||||
{
|
||||
relatedSavedObjects: [
|
||||
|
@ -294,10 +288,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not overwrite existing references if relatedSavedObjects is undefined', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({}, [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
|
@ -324,10 +317,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not overwrite existing references if relatedSavedObjects is empty', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData({ relatedSavedObjects: [] }, [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
|
@ -360,7 +352,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.0.0', () => {
|
||||
test('no op migration for rules SO', () => {
|
||||
const migration800 = getActionTaskParamsMigrations(encryptedSavedObjectsSetup, [])['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, [])['8.0.0']
|
||||
);
|
||||
const actionTaskParam = getMockData();
|
||||
expect(migration800(actionTaskParam, context)).toEqual(actionTaskParam);
|
||||
});
|
||||
|
@ -377,10 +371,9 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.16.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionTaskParamsMigrations(encryptedSavedObjectsSetup, preconfiguredActions)['7.16.0']
|
||||
);
|
||||
const actionTaskParam = getMockData();
|
||||
expect(() => {
|
||||
migration716(actionTaskParam, context);
|
||||
|
|
|
@ -11,6 +11,7 @@ import { RawAction } from '../types';
|
|||
import { SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
|
||||
import { migrationMocks } from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
|
||||
const context = migrationMocks.createContext();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
|
@ -23,7 +24,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.10.0', () => {
|
||||
test('add hasAuth config property for .email actions', () => {
|
||||
const migration710 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0']
|
||||
);
|
||||
const action = getMockDataForEmail({});
|
||||
const migratedAction = migration710(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -41,7 +44,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('rename cases configuration object', () => {
|
||||
const migration710 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0']
|
||||
);
|
||||
const action = getCasesMockData({});
|
||||
const migratedAction = migration710(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -61,7 +66,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.11.0', () => {
|
||||
test('add hasAuth = true for .webhook actions with user and password', () => {
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0']
|
||||
);
|
||||
const action = getMockDataForWebhook({}, true);
|
||||
expect(migration711(action, context)).toMatchObject({
|
||||
...action,
|
||||
|
@ -75,7 +82,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('add hasAuth = false for .webhook actions without user and password', () => {
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0']
|
||||
);
|
||||
const action = getMockDataForWebhook({}, false);
|
||||
expect(migration711(action, context)).toMatchObject({
|
||||
...action,
|
||||
|
@ -88,7 +97,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
});
|
||||
test('remove cases mapping object', () => {
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0']
|
||||
);
|
||||
const action = getMockData({
|
||||
config: { incidentConfiguration: { mapping: [] }, isCaseOwned: true, another: 'value' },
|
||||
});
|
||||
|
@ -106,7 +117,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.14.0', () => {
|
||||
test('add isMissingSecrets property for actions', () => {
|
||||
const migration714 = getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const migration714 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0']
|
||||
);
|
||||
const action = getMockData({ isMissingSecrets: undefined });
|
||||
const migratedAction = migration714(action, context);
|
||||
expect(migratedAction).toEqual({
|
||||
|
@ -121,7 +134,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.16.0', () => {
|
||||
test('set service config property for .email connectors if service is undefined', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockDataForEmail({ config: { service: undefined } });
|
||||
const migratedAction = migration716(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -139,7 +154,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('set service config property for .email connectors if service is null', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockDataForEmail({ config: { service: null } });
|
||||
const migratedAction = migration716(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -157,7 +174,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('skips migrating .email connectors if service is defined, even if value is nonsense', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockDataForEmail({ config: { service: 'gobbledygook' } });
|
||||
const migratedAction = migration716(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -167,7 +186,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('set usesTableApi config property for .servicenow', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockDataForServiceNow716({ usesTableApi: true });
|
||||
const migratedAction = migration716(action, context);
|
||||
|
||||
|
@ -184,7 +205,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('set usesTableApi config property for .servicenow-sir', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockDataForServiceNow716({ actionTypeId: '.servicenow-sir' });
|
||||
const migratedAction = migration716(action, context);
|
||||
|
||||
|
@ -201,7 +224,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it does not set usesTableApi config for other connectors', () => {
|
||||
const migration716 = getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.16.0']
|
||||
);
|
||||
const action = getMockData();
|
||||
const migratedAction = migration716(action, context);
|
||||
expect(migratedAction).toEqual(action);
|
||||
|
@ -210,7 +235,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.0.0', () => {
|
||||
test('no op migration for rules SO', () => {
|
||||
const migration800 = getActionsMigrations(encryptedSavedObjectsSetup)['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['8.0.0']
|
||||
);
|
||||
const action = getMockData({});
|
||||
expect(migration800(action, context)).toEqual(action);
|
||||
});
|
||||
|
@ -218,7 +245,9 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.3.0', () => {
|
||||
test('set isOAuth config property for .servicenow', () => {
|
||||
const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0'];
|
||||
const migration830 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']
|
||||
);
|
||||
const action = getMockDataForServiceNow83();
|
||||
const migratedAction = migration830(action, context);
|
||||
|
||||
|
@ -230,7 +259,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('set isOAuth config property for .servicenow-sir', () => {
|
||||
const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0'];
|
||||
const migration830 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']
|
||||
);
|
||||
const action = getMockDataForServiceNow83({ actionTypeId: '.servicenow-sir' });
|
||||
const migratedAction = migration830(action, context);
|
||||
|
||||
|
@ -242,7 +273,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('set isOAuth config property for .servicenow-itom', () => {
|
||||
const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0'];
|
||||
const migration830 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']
|
||||
);
|
||||
const action = getMockDataForServiceNow83({ actionTypeId: '.servicenow-itom' });
|
||||
const migratedAction = migration830(action, context);
|
||||
|
||||
|
@ -254,7 +287,9 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it does not set isOAuth config for other connectors', () => {
|
||||
const migration830 = getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0'];
|
||||
const migration830 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['8.3.0']
|
||||
);
|
||||
const action = getMockData();
|
||||
const migratedAction = migration830(action, context);
|
||||
|
||||
|
@ -273,7 +308,9 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.10.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration710 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0']
|
||||
);
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration710(action, context);
|
||||
|
@ -291,7 +328,9 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.11.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0']
|
||||
);
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration711(action, context);
|
||||
|
@ -309,7 +348,9 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.14.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration714 = getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const migration714 = SavedObjectsUtils.getMigrationFunction(
|
||||
getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0']
|
||||
);
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration714(action, context);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"@kbn/safer-lodash-set",
|
||||
"@kbn/core-http-server-mocks",
|
||||
"@kbn/tinymath",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,7 @@ import type {
|
|||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core/server';
|
||||
import { mergeSavedObjectMigrationMaps } from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import type { MigrateFunction, MigrateFunctionsObject } from '@kbn/kibana-utils-plugin/common';
|
||||
import type { SerializableRecord } from '@kbn/utility-types';
|
||||
import { GENERATED_ALERT, SUB_CASE_SAVED_OBJECT } from './constants';
|
||||
|
@ -245,7 +246,10 @@ describe('comments migrations', () => {
|
|||
});
|
||||
|
||||
expect(migrations['7.14.0']).toBeDefined();
|
||||
const result = migrations['7.14.0'](caseComment, contextMock);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.14.0'])(
|
||||
caseComment,
|
||||
contextMock
|
||||
);
|
||||
|
||||
const parsedComment = parseCommentString(result.attributes.comment);
|
||||
const lensVisualizations = getLensVisualizations(
|
||||
|
@ -330,7 +334,7 @@ describe('comments migrations', () => {
|
|||
};
|
||||
|
||||
const mergedFunctions = mergeSavedObjectMigrationMaps(migrationObj1, migrationObj2);
|
||||
mergedFunctions['1.0.0'](caseComment, contextMock);
|
||||
SavedObjectsUtils.getMigrationFunction(mergedFunctions['1.0.0'])(caseComment, contextMock);
|
||||
|
||||
const log = contextMock.log as jest.Mocked<SavedObjectsMigrationLogger>;
|
||||
expect(log.error.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
|
@ -360,7 +364,9 @@ describe('comments migrations', () => {
|
|||
|
||||
const mergedFunctions = mergeSavedObjectMigrationMaps(migrationObj1, migrationObj2);
|
||||
|
||||
expect(() => mergedFunctions['2.0.0'](caseComment, contextMock)).toThrow();
|
||||
expect(() =>
|
||||
SavedObjectsUtils.getMigrationFunction(mergedFunctions['2.0.0'])(caseComment, contextMock)
|
||||
).toThrow();
|
||||
|
||||
const log = contextMock.log as jest.Mocked<SavedObjectsMigrationLogger>;
|
||||
expect(log.error).not.toHaveBeenCalled();
|
||||
|
@ -586,7 +592,7 @@ describe('comments migrations', () => {
|
|||
});
|
||||
|
||||
it('migrates a persistable state attachment correctly', () => {
|
||||
const migrationFn = migrations['8.4.0'];
|
||||
const migrationFn = SavedObjectsUtils.getMigrationFunction(migrations['8.4.0']);
|
||||
const res = migrationFn(
|
||||
{
|
||||
id: '123',
|
||||
|
@ -617,7 +623,7 @@ describe('comments migrations', () => {
|
|||
});
|
||||
|
||||
it('should not change any other attribute expect persistableStateAttachmentState or put excess attributes', () => {
|
||||
const migrationFn = migrations['8.4.0'];
|
||||
const migrationFn = SavedObjectsUtils.getMigrationFunction(migrations['8.4.0']);
|
||||
const res = migrationFn(
|
||||
{
|
||||
id: '123',
|
||||
|
@ -655,7 +661,7 @@ describe('comments migrations', () => {
|
|||
* combined along with the persistable state attachment
|
||||
* migrations
|
||||
*/
|
||||
const migrationFn = migrations['7.14.0'];
|
||||
const migrationFn = SavedObjectsUtils.getMigrationFunction(migrations['7.14.0']);
|
||||
const res = migrationFn(
|
||||
{
|
||||
id: '123',
|
||||
|
@ -688,7 +694,7 @@ describe('comments migrations', () => {
|
|||
});
|
||||
|
||||
it('does not run persistable state migration on other attachments', () => {
|
||||
const migrationFn = migrations['8.4.0'];
|
||||
const migrationFn = SavedObjectsUtils.getMigrationFunction(migrations['8.4.0']);
|
||||
const res = migrationFn(
|
||||
{
|
||||
id: '123',
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
SavedObjectMigrationFn,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
} from '@kbn/core/server';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import {
|
||||
LensDocShape715,
|
||||
LensDocShape810,
|
||||
|
@ -132,12 +133,12 @@ describe('Lens migrations', () => {
|
|||
visualizationType: 'lnsMetric',
|
||||
},
|
||||
};
|
||||
const result = migrations['7.7.0'](target, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.7.0'])(target, context);
|
||||
expect(result).toEqual(target);
|
||||
});
|
||||
|
||||
it('should handle missing layers', () => {
|
||||
const result = migrations['7.7.0'](
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.7.0'])(
|
||||
{
|
||||
...example,
|
||||
attributes: {
|
||||
|
@ -169,7 +170,7 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should remove only missing accessors', () => {
|
||||
const result = migrations['7.7.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.7.0'])(example, context);
|
||||
|
||||
expect(result.attributes.state.visualization.layers).toEqual([
|
||||
{
|
||||
|
@ -285,7 +286,7 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('should remove the lens_auto_date expression', () => {
|
||||
const result = migrations['7.8.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.8.0'])(example, context);
|
||||
expect(result.attributes.expression).toContain(`timeFields=\"products.created_on\"`);
|
||||
});
|
||||
|
||||
|
@ -302,7 +303,7 @@ describe('Lens migrations', () => {
|
|||
| xyVis xTitle="products.created_on" yTitle="Count of records" legend={legendConfig isVisible=true position="right"} layers={}`,
|
||||
},
|
||||
};
|
||||
const result = migrations['7.8.0'](input, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.8.0'])(input, context);
|
||||
expect(result).toEqual(input);
|
||||
});
|
||||
});
|
||||
|
@ -454,12 +455,12 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('should remove expression', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(result.attributes.expression).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should list references for layers', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(
|
||||
result.references?.find(
|
||||
(ref) => ref.name === 'indexpattern-datasource-layer-3b7791e9-326e-40d5-a787-b7594e48d906'
|
||||
|
@ -473,7 +474,7 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should remove index pattern ids from layers', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(
|
||||
result.attributes.state.datasourceStates.indexpattern.layers[
|
||||
'3b7791e9-326e-40d5-a787-b7594e48d906'
|
||||
|
@ -487,12 +488,12 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should remove datsource meta data', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(result.attributes.state.datasourceMetaData).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should list references for filters', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(result.references?.find((ref) => ref.name === 'filter-index-pattern-0')?.id).toEqual(
|
||||
'90943e30-9a47-11e8-b64d-95841ca0b247'
|
||||
);
|
||||
|
@ -502,7 +503,7 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should remove index pattern ids from filters', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(result.attributes.state.filters[0].meta.index).toBeUndefined();
|
||||
expect(result.attributes.state.filters[0].meta.indexRefName).toEqual(
|
||||
'filter-index-pattern-0'
|
||||
|
@ -514,7 +515,7 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should list reference for current index pattern', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(
|
||||
result.references?.find(
|
||||
(ref) => ref.name === 'indexpattern-datasource-current-indexpattern'
|
||||
|
@ -523,14 +524,14 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should remove current index pattern id from datasource state', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
expect(
|
||||
result.attributes.state.datasourceStates.indexpattern.currentIndexPatternId
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should produce a valid document', () => {
|
||||
const result = migrations['7.10.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.10.0'])(example, context);
|
||||
// changes to the outcome of this are critical - this test is a safe guard to not introduce changes accidentally
|
||||
// if this test fails, make extra sure it's expected
|
||||
expect(result).toMatchSnapshot();
|
||||
|
@ -608,9 +609,10 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('should remove the suggested priority from all columns', () => {
|
||||
const result = migrations['7.11.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.11.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const resultLayers = result.attributes.state.datasourceStates.indexpattern.layers;
|
||||
const layersWithSuggestedPriority = Object.values(resultLayers).reduce(
|
||||
(count, layer) =>
|
||||
|
@ -657,16 +659,18 @@ describe('Lens migrations', () => {
|
|||
...example,
|
||||
attributes: { ...example.attributes, visualizationType: 'xy' },
|
||||
};
|
||||
const result = migrations['7.12.0'](xyChart, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.12.0'])(
|
||||
xyChart,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toBe(xyChart);
|
||||
});
|
||||
|
||||
it('should remove layer array and reshape state', () => {
|
||||
const result = migrations['7.12.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.12.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result.attributes.state.visualization).toEqual({
|
||||
layerId: 'first',
|
||||
columns: [
|
||||
|
@ -856,19 +860,22 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('should rename only specific operation types', () => {
|
||||
const result = migrations['7.13.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.13.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
validate(result);
|
||||
});
|
||||
|
||||
it('can be applied multiple times', () => {
|
||||
const result1 = migrations['7.13.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result2 = migrations['7.13.1'](result1, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result1 = SavedObjectsUtils.getMigrationFunction(migrations['7.13.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const result2 = SavedObjectsUtils.getMigrationFunction(migrations['7.13.1'])(
|
||||
result1,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
validate(result2);
|
||||
});
|
||||
});
|
||||
|
@ -949,9 +956,10 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('should remove time zone param from date histogram', () => {
|
||||
const result = migrations['7.14.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.14.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const layers = Object.values(result.attributes.state.datasourceStates.indexpattern.layers);
|
||||
expect(layers.length).toBe(1);
|
||||
const columns = Object.values(layers[0].columns);
|
||||
|
@ -1059,9 +1067,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisStatePre715;
|
||||
const result = migrations['7.15.0'](xyExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.15.0'])(
|
||||
xyExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (result.attributes as LensDocShape715<VisStatePost715>).state.visualization;
|
||||
if ('layers' in state) {
|
||||
for (const layer of state.layers) {
|
||||
|
@ -1087,9 +1096,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisStatePre715;
|
||||
const result = migrations['7.15.0'](pieExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.15.0'])(
|
||||
pieExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (result.attributes as LensDocShape715<VisStatePost715>).state.visualization;
|
||||
if ('layers' in state) {
|
||||
for (const layer of state.layers) {
|
||||
|
@ -1104,9 +1114,10 @@ describe('Lens migrations', () => {
|
|||
layerId: '1',
|
||||
accessor: undefined,
|
||||
} as unknown as VisStatePre715;
|
||||
const result = migrations['7.15.0'](metricExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.15.0'])(
|
||||
metricExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (result.attributes as LensDocShape715<VisStatePost715>).state.visualization;
|
||||
expect('layerType' in state).toEqual(true);
|
||||
if ('layerType' in state) {
|
||||
|
@ -1120,9 +1131,10 @@ describe('Lens migrations', () => {
|
|||
layerId: '1',
|
||||
accessor: undefined,
|
||||
} as unknown as VisStatePre715;
|
||||
const result = migrations['7.15.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.15.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (result.attributes as LensDocShape715<VisStatePost715>).state.visualization;
|
||||
expect('layerType' in state).toEqual(true);
|
||||
if ('layerType' in state) {
|
||||
|
@ -1136,9 +1148,10 @@ describe('Lens migrations', () => {
|
|||
layerId: '1',
|
||||
accessor: undefined,
|
||||
} as unknown as VisStatePre715;
|
||||
const result = migrations['7.15.0'](heatmapExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.15.0'])(
|
||||
heatmapExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (result.attributes as LensDocShape715<VisStatePost715>).state.visualization;
|
||||
expect('layerType' in state).toEqual(true);
|
||||
if ('layerType' in state) {
|
||||
|
@ -1221,9 +1234,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](exampleCopy, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
exampleCopy,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toEqual(exampleCopy);
|
||||
}
|
||||
});
|
||||
|
@ -1241,9 +1255,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toEqual(datatableExample);
|
||||
});
|
||||
|
||||
|
@ -1260,9 +1275,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toEqual(datatableExample);
|
||||
});
|
||||
|
||||
|
@ -1272,9 +1288,10 @@ describe('Lens migrations', () => {
|
|||
(datatableExample.attributes as LensDocShape715<VisState716>).state.visualization = {
|
||||
columns: [{ colorMode: 'none' }, {}],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toEqual(datatableExample);
|
||||
});
|
||||
|
||||
|
@ -1291,9 +1308,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result).toEqual(datatableExample);
|
||||
});
|
||||
|
||||
|
@ -1340,9 +1358,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
],
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (
|
||||
result.attributes as LensDocShape715<Extract<VisState716, { columns: unknown[] }>>
|
||||
).state.visualization;
|
||||
|
@ -1390,9 +1409,10 @@ describe('Lens migrations', () => {
|
|||
},
|
||||
},
|
||||
} as unknown as VisState716;
|
||||
const result = migrations['7.16.0'](datatableExample, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['7.16.0'])(
|
||||
datatableExample,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const state = (
|
||||
result.attributes as LensDocShape715<
|
||||
Extract<VisState716, { palette?: PaletteOutput<CustomPaletteParams> }>
|
||||
|
@ -1519,9 +1539,10 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const result = migrations['8.1.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.1.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
||||
expect(result.attributes.state.filters).toEqual(expectedFilters);
|
||||
});
|
||||
|
@ -1586,9 +1607,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('should change field for count operations but not for others, not changing the vis', () => {
|
||||
const result = migrations['8.1.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.1.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
||||
expect(
|
||||
Object.values(
|
||||
|
@ -1632,10 +1654,9 @@ describe('Lens migrations', () => {
|
|||
{}
|
||||
);
|
||||
|
||||
const migratedLensDoc = migrationFunctionsObject[migrationVersion](
|
||||
lensVisualizationDoc as SavedObjectUnsanitizedDoc,
|
||||
{} as SavedObjectMigrationContext
|
||||
);
|
||||
const migratedLensDoc = SavedObjectsUtils.getMigrationFunction(
|
||||
migrationFunctionsObject[migrationVersion]
|
||||
)(lensVisualizationDoc as SavedObjectUnsanitizedDoc, {} as SavedObjectMigrationContext);
|
||||
|
||||
expect(migratedLensDoc).toEqual({
|
||||
attributes: {
|
||||
|
@ -1687,10 +1708,9 @@ describe('Lens migrations', () => {
|
|||
{}
|
||||
);
|
||||
|
||||
const migratedLensDoc = migrationFunctionsObject[migrationVersion](
|
||||
lensVisualizationDoc as SavedObjectUnsanitizedDoc,
|
||||
{} as SavedObjectMigrationContext
|
||||
);
|
||||
const migratedLensDoc = SavedObjectsUtils.getMigrationFunction(
|
||||
migrationFunctionsObject[migrationVersion]
|
||||
)(lensVisualizationDoc as SavedObjectUnsanitizedDoc, {} as SavedObjectMigrationContext);
|
||||
|
||||
expect(migratedLensDoc).toEqual({
|
||||
attributes: {
|
||||
|
@ -1735,11 +1755,12 @@ describe('Lens migrations', () => {
|
|||
}),
|
||||
}
|
||||
);
|
||||
const migratedLensDoc = migrationFunctionsObject[migrationVersion](
|
||||
lensVisualizationDoc as SavedObjectUnsanitizedDoc,
|
||||
{} as SavedObjectMigrationContext
|
||||
);
|
||||
const otherLensDoc = migrationFunctionsObject[migrationVersion](
|
||||
const migratedLensDoc = SavedObjectsUtils.getMigrationFunction(
|
||||
migrationFunctionsObject[migrationVersion]
|
||||
)(lensVisualizationDoc as SavedObjectUnsanitizedDoc, {} as SavedObjectMigrationContext);
|
||||
const otherLensDoc = SavedObjectsUtils.getMigrationFunction(
|
||||
migrationFunctionsObject[migrationVersion]
|
||||
)(
|
||||
{
|
||||
...lensVisualizationDoc,
|
||||
attributes: {
|
||||
|
@ -1829,9 +1850,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('should change field for count operations but not for others, not changing the vis', () => {
|
||||
const result = migrations['8.1.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.1.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
||||
expect(
|
||||
Object.values(
|
||||
|
@ -1924,9 +1946,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('should set showArrayValues for last-value columns', () => {
|
||||
const result = migrations['8.2.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.2.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
||||
const layer2Columns =
|
||||
result.attributes.state.datasourceStates.indexpattern.layers['2'].columns;
|
||||
|
@ -2031,7 +2054,10 @@ describe('Lens migrations', () => {
|
|||
}
|
||||
|
||||
it('should migrate enabled fitRowToContent to new rowHeight: "auto"', () => {
|
||||
const result = migrations['8.2.0'](getExample(true), context) as ReturnType<
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.2.0'])(
|
||||
getExample(true),
|
||||
context
|
||||
) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape810<VisState810>, LensDocShape810<VisState820>>
|
||||
>;
|
||||
|
||||
|
@ -2043,7 +2069,10 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('should migrate disabled fitRowToContent to new rowHeight: "single"', () => {
|
||||
const result = migrations['8.2.0'](getExample(false), context) as ReturnType<
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.2.0'])(
|
||||
getExample(false),
|
||||
context
|
||||
) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape810<VisState810>, LensDocShape810<VisState820>>
|
||||
>;
|
||||
|
||||
|
@ -2117,9 +2146,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('should set include empty rows for all date histogram columns', () => {
|
||||
const result = migrations['8.2.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.2.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
||||
const layer2Columns =
|
||||
result.attributes.state.datasourceStates.indexpattern.layers['2'].columns;
|
||||
|
@ -2145,9 +2175,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('preserves current config for existing visualizations that are using the DEFAULTS', () => {
|
||||
const result = migrations['8.3.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.3.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const visState = result.attributes.state.visualization as LegacyMetricState;
|
||||
expect(visState.textAlign).toBe('center');
|
||||
expect(visState.titlePosition).toBe('bottom');
|
||||
|
@ -2155,7 +2186,7 @@ describe('Lens migrations', () => {
|
|||
});
|
||||
|
||||
it('preserves current config for existing visualizations that are using CUSTOM settings', () => {
|
||||
const result = migrations['8.3.0'](
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.3.0'])(
|
||||
{
|
||||
...example,
|
||||
attributes: {
|
||||
|
@ -2180,7 +2211,7 @@ describe('Lens migrations', () => {
|
|||
|
||||
describe('8.3.0 - convert legend sizes to strings', () => {
|
||||
const context = { log: { warn: () => {} } } as unknown as SavedObjectMigrationContext;
|
||||
const migrate = migrations['8.3.0'];
|
||||
const migrate = SavedObjectsUtils.getMigrationFunction(migrations['8.3.0']);
|
||||
|
||||
const autoLegendSize = 'auto';
|
||||
const largeLegendSize = 'large';
|
||||
|
@ -2266,15 +2297,16 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('migrates valueLabels from `inside` to `show`', () => {
|
||||
const result = migrations['8.3.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.3.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const visState = result.attributes.state.visualization as VisState830;
|
||||
expect(visState.valueLabels).toBe('show');
|
||||
});
|
||||
|
||||
it("doesn't migrate valueLabels with `hide` value", () => {
|
||||
const result = migrations['8.3.0'](
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.3.0'])(
|
||||
{
|
||||
...example,
|
||||
attributes: {
|
||||
|
@ -2320,9 +2352,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape850<XYVisStatePre850>>;
|
||||
|
||||
it('migrates existing annotation events as manual type', () => {
|
||||
const result = migrations['8.5.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.5.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
const visState = result.attributes.state.visualization as VisState850;
|
||||
const [dataLayer, annotationLayer] = visState.layers;
|
||||
expect(dataLayer).toEqual({ layerType: 'data' });
|
||||
|
@ -2349,14 +2382,15 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('lnsMetric => lnsLegacyMetric', () => {
|
||||
const result = migrations['8.5.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.5.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(result.attributes.visualizationType).toBe('lnsLegacyMetric');
|
||||
});
|
||||
|
||||
it('lnsMetricNew => lnsMetric', () => {
|
||||
const result = migrations['8.5.0'](
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.5.0'])(
|
||||
{ ...example, attributes: { ...example.attributes, visualizationType: 'lnsMetricNew' } },
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
|
@ -2390,9 +2424,10 @@ describe('Lens migrations', () => {
|
|||
} as unknown as SavedObjectUnsanitizedDoc<LensDocShape810>;
|
||||
|
||||
it('make metric an array', () => {
|
||||
const result = migrations['8.6.0'](example, context) as ReturnType<
|
||||
SavedObjectMigrationFn<LensDocShape, LensDocShape>
|
||||
>;
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.6.0'])(
|
||||
example,
|
||||
context
|
||||
) as ReturnType<SavedObjectMigrationFn<LensDocShape, LensDocShape>>;
|
||||
expect(
|
||||
(result.attributes.state.visualization as { layers: Array<{ metrics: string[] }> })
|
||||
.layers[0]
|
||||
|
@ -2496,7 +2531,7 @@ describe('Lens migrations', () => {
|
|||
};
|
||||
|
||||
it('migrates the indexpattern datasource to formBased', () => {
|
||||
const result = migrations['8.6.0'](example, context);
|
||||
const result = SavedObjectsUtils.getMigrationFunction(migrations['8.6.0'])(example, context);
|
||||
expect(result.attributes.state.datasourceStates.formBased).toBe(
|
||||
example.attributes.state.datasourceStates.indexpattern
|
||||
);
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"@kbn/core-saved-objects-api-server",
|
||||
"@kbn/object-versioning",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type { SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { migrationMocks } from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { ManifestConstants } from './common';
|
||||
import type { OldInternalManifestSchema } from './migrations';
|
||||
import { migrations } from './migrations';
|
||||
|
@ -21,7 +22,7 @@ describe('7.12.0 manifest migrations', () => {
|
|||
const ARTIFACT_ID_3 =
|
||||
'endpoint-trustlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3';
|
||||
|
||||
const migration = migrations['7.12.0'];
|
||||
const migration = SavedObjectsUtils.getMigrationFunction(migrations['7.12.0']);
|
||||
|
||||
test('Migrates ids property', () => {
|
||||
const doc: SavedObjectUnsanitizedDoc<OldInternalManifestSchema> = {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import { getMigrations } from './migrations';
|
||||
import { SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { migrationMocks } from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import { TaskInstanceWithDeprecatedFields } from '../task';
|
||||
|
||||
const migrationContext = migrationMocks.createContext();
|
||||
|
@ -19,7 +20,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
describe('7.4.0', () => {
|
||||
test('extend task instance with updated_at', () => {
|
||||
const migration740 = getMigrations()['7.4.0'];
|
||||
const migration740 = SavedObjectsUtils.getMigrationFunction(getMigrations()['7.4.0']);
|
||||
const taskInstance = getMockData({});
|
||||
expect(migration740(taskInstance, migrationContext).attributes.updated_at).not.toBeNull();
|
||||
});
|
||||
|
@ -27,7 +28,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.6.0', () => {
|
||||
test('rename property Internal to Schedule', () => {
|
||||
const migration760 = getMigrations()['7.6.0'];
|
||||
const migration760 = SavedObjectsUtils.getMigrationFunction(getMigrations()['7.6.0']);
|
||||
const taskInstance = getMockData({});
|
||||
expect(migration760(taskInstance, migrationContext)).toEqual({
|
||||
...taskInstance,
|
||||
|
@ -41,7 +42,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.0.0', () => {
|
||||
test('transforms actionsTasksLegacyIdToSavedObjectIds', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'actions:123456',
|
||||
params: JSON.stringify({ spaceId: 'user1', actionTaskParamsId: '123456' }),
|
||||
|
@ -57,7 +58,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it is only applicable for saved objects that live in a custom space', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'actions:123456',
|
||||
params: JSON.stringify({ spaceId: 'default', actionTaskParamsId: '123456' }),
|
||||
|
@ -67,7 +68,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it is only applicable for saved objects that live in a custom space even if spaces are disabled', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'actions:123456',
|
||||
params: JSON.stringify({ actionTaskParamsId: '123456' }),
|
||||
|
@ -77,7 +78,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('transforms alertingTaskLegacyIdToSavedObjectIds', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123456',
|
||||
params: JSON.stringify({ spaceId: 'user1', alertId: '123456' }),
|
||||
|
@ -93,7 +94,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('skip transformation for defult space scenario', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123456',
|
||||
params: JSON.stringify({ spaceId: 'default', alertId: '123456' }),
|
||||
|
@ -111,7 +112,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.2.0', () => {
|
||||
test('resets attempts and status of a "failed" alerting tasks without schedule interval', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123',
|
||||
status: 'failed',
|
||||
|
@ -129,7 +130,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('resets attempts and status of a "running" alerting tasks without schedule interval', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123',
|
||||
status: 'running',
|
||||
|
@ -147,7 +148,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not update the tasks that are not "failed"', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123',
|
||||
status: 'idle',
|
||||
|
@ -159,7 +160,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not update the tasks that are not "failed" and has a schedule', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123',
|
||||
status: 'idle',
|
||||
|
@ -171,7 +172,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('resets "unrecognized" status to "idle" when task type is not in REMOVED_TYPES list', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'someValidTask',
|
||||
status: 'unrecognized',
|
||||
|
@ -187,7 +188,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not modify "unrecognized" status when task type is in REMOVED_TYPES list', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'sampleTaskRemovedType',
|
||||
status: 'unrecognized',
|
||||
|
@ -197,7 +198,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not modify document when status is "running"', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'someTask',
|
||||
status: 'running',
|
||||
|
@ -207,7 +208,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not modify document when status is "idle"', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'someTask',
|
||||
status: 'idle',
|
||||
|
@ -217,7 +218,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not modify document when status is "failed"', () => {
|
||||
const migration820 = getMigrations()['8.2.0'];
|
||||
const migration820 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.2.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'someTask',
|
||||
status: 'failed',
|
||||
|
@ -229,7 +230,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('8.5.0', () => {
|
||||
test('adds enabled: true to tasks that are running, claiming, or idle', () => {
|
||||
const migration850 = getMigrations()['8.5.0'];
|
||||
const migration850 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.5.0']);
|
||||
const activeTasks = [
|
||||
getMockData({
|
||||
status: 'running',
|
||||
|
@ -253,7 +254,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('does not modify tasks that are failed or unrecognized', () => {
|
||||
const migration850 = getMigrations()['8.5.0'];
|
||||
const migration850 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.5.0']);
|
||||
const inactiveTasks = [
|
||||
getMockData({
|
||||
status: 'failed',
|
||||
|
@ -272,7 +273,7 @@ describe('successful migrations', () => {
|
|||
describe('handles errors during migrations', () => {
|
||||
describe('8.0.0 throws if migration fails', () => {
|
||||
test('should throw the exception if task instance params format is wrong', () => {
|
||||
const migration800 = getMigrations()['8.0.0'];
|
||||
const migration800 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.0.0']);
|
||||
const taskInstance = getMockData({
|
||||
taskType: 'alerting:123456',
|
||||
params: `{ spaceId: 'user1', customId: '123456' }`,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { omit, cloneDeep } from 'lodash';
|
||||
import { SavedObjectUnsanitizedDoc } from '@kbn/core/server';
|
||||
import { migrationMocks } from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server';
|
||||
import type {
|
||||
RuleTaskState,
|
||||
WrappedLifecycleRuleState,
|
||||
|
@ -20,7 +21,7 @@ import { SerializedConcreteTaskInstance, TaskStatus } from '../task';
|
|||
type RawAlertInstances = Record<string, RawAlertInstance>;
|
||||
|
||||
const migrationContext = migrationMocks.createContext();
|
||||
const migration880 = getMigrations()['8.8.0'];
|
||||
const migration880 = SavedObjectsUtils.getMigrationFunction(getMigrations()['8.8.0']);
|
||||
|
||||
describe('successful migrations for 8.8.0', () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
"@kbn/safer-lodash-set",
|
||||
"@kbn/es-types",
|
||||
"@kbn/apm-utils",
|
||||
"@kbn/core-saved-objects-common"
|
||||
"@kbn/core-saved-objects-common",
|
||||
"@kbn/core-saved-objects-utils-server"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue