mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
SO model versions: add validation for mapping additions (#158714)
## Summary Add validation to ensure that mappings added via a `mappings_addition` type change are also present in the type's full mappings. (just an extra layer of protection for when the teams will switch to using the new API)
This commit is contained in:
parent
0f7eca4d68
commit
109e67b464
2 changed files with 164 additions and 1 deletions
|
@ -236,6 +236,103 @@ describe('validateTypeMigrations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('modelVersions mapping additions', () => {
|
||||
it('throws when registering mapping additions not present in the global mappings', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.8.0',
|
||||
modelVersions: {
|
||||
'1': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
field2: { type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
field1: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Type foo: mappings added on model versions not present on the global mappings definition: field2.type"`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not throw when registering mapping additions are present in the global mappings', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.8.0',
|
||||
modelVersions: {
|
||||
'1': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
field2: { type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
'2': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
field3: { type: 'text' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
field1: { type: 'text' },
|
||||
field2: { type: 'text' },
|
||||
field3: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).not.toThrow();
|
||||
});
|
||||
|
||||
it('throws when registering mapping additions different than the global mappings', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.8.0',
|
||||
modelVersions: {
|
||||
'1': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
field2: { type: 'boolean' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
field1: { type: 'text' },
|
||||
field2: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Type foo: mappings added on model versions differs from the global mappings definition: field2.type"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToMultiNamespaceTypeVersion', () => {
|
||||
it(`validates convertToMultiNamespaceTypeVersion can only be used with namespaceType 'multiple' or 'multiple-isolated'`, () => {
|
||||
const type = createType({
|
||||
|
|
|
@ -7,9 +7,18 @@
|
|||
*/
|
||||
|
||||
import Semver from 'semver';
|
||||
import { getFlattenedObject } from '@kbn/std';
|
||||
import type { SavedObjectsNamespaceType } from '@kbn/core-saved-objects-common';
|
||||
import type { SavedObjectsType } from '@kbn/core-saved-objects-server';
|
||||
import type {
|
||||
SavedObjectsType,
|
||||
SavedObjectsTypeMappingDefinition,
|
||||
SavedObjectsModelVersionMap,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
import { assertValidModelVersion } from '@kbn/core-saved-objects-base-server-internal';
|
||||
import {
|
||||
SavedObjectsModelChange,
|
||||
SavedObjectsModelMappingsAdditionChange,
|
||||
} from '@kbn/core-saved-objects-server';
|
||||
|
||||
/**
|
||||
* Validates the consistency of the given type for use with the document migrator.
|
||||
|
@ -87,6 +96,9 @@ export function validateTypeMigrations({
|
|||
if (minVersion > 1) {
|
||||
throw new Error(`Type ${type.name}: model versioning must start with version 1`);
|
||||
}
|
||||
|
||||
validateAddedMappings(type.name, type.mappings, modelVersionMap);
|
||||
|
||||
const missingVersions = getMissingVersions(
|
||||
minVersion,
|
||||
maxVersion,
|
||||
|
@ -114,6 +126,60 @@ export function validateTypeMigrations({
|
|||
}
|
||||
}
|
||||
|
||||
function isMappingAddition(
|
||||
change: SavedObjectsModelChange
|
||||
): change is SavedObjectsModelMappingsAdditionChange {
|
||||
return change.type === 'mappings_addition';
|
||||
}
|
||||
|
||||
const validateAddedMappings = (
|
||||
typeName: string,
|
||||
mappings: SavedObjectsTypeMappingDefinition,
|
||||
modelVersions: SavedObjectsModelVersionMap
|
||||
) => {
|
||||
const flattenedMappings = new Map(Object.entries(getFlattenedObject(mappings.properties)));
|
||||
|
||||
const mappingAdditionChanges = Object.values(modelVersions)
|
||||
.flatMap((version) => version.changes)
|
||||
.filter<SavedObjectsModelMappingsAdditionChange>(isMappingAddition);
|
||||
const addedMappings = mappingAdditionChanges.reduce((map, change) => {
|
||||
const flattened = getFlattenedObject(change.addedMappings);
|
||||
Object.keys(flattened).forEach((key) => {
|
||||
map.set(key, flattened[key]);
|
||||
});
|
||||
return map;
|
||||
}, new Map<string, unknown>());
|
||||
|
||||
const missingMappings: string[] = [];
|
||||
const mappingsWithDifferentValues: string[] = [];
|
||||
|
||||
for (const [key, value] of addedMappings.entries()) {
|
||||
if (!flattenedMappings.has(key)) {
|
||||
missingMappings.push(key);
|
||||
} else {
|
||||
const valueInMappings = flattenedMappings.get(key);
|
||||
if (valueInMappings !== value) {
|
||||
mappingsWithDifferentValues.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingMappings.length) {
|
||||
throw new Error(
|
||||
`Type ${typeName}: mappings added on model versions not present on the global mappings definition: ${missingMappings.join(
|
||||
','
|
||||
)}`
|
||||
);
|
||||
}
|
||||
if (mappingsWithDifferentValues.length) {
|
||||
throw new Error(
|
||||
`Type ${typeName}: mappings added on model versions differs from the global mappings definition: ${mappingsWithDifferentValues.join(
|
||||
','
|
||||
)}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const assertObjectOrFunction = (entity: any, prefix: string) => {
|
||||
if (!entity || (typeof entity !== 'function' && typeof entity !== 'object')) {
|
||||
throw new Error(`${prefix} Got! ${typeof entity}, ${JSON.stringify(entity)}.`);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue