mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Prevent so type registration if schema declared directly (#177246)
Fix https://github.com/elastic/kibana/issues/176668 ## Summary After a switch to model versions, saved object registrations are blocked if any schema for a higher version is declared when not coupled with a model version. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
6d44340e52
commit
bf4b70ceb4
4 changed files with 167 additions and 4 deletions
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SavedObjectsType, SavedObjectsModelVersion } from '@kbn/core-saved-objects-server';
|
||||
import { validateTypeMigrations } from './validate_migrations';
|
||||
|
||||
|
@ -50,6 +51,9 @@ describe('validateTypeMigrations', () => {
|
|||
bar: jest.fn(),
|
||||
'1.2.3': jest.fn(),
|
||||
},
|
||||
schemas: {
|
||||
'1.2.3': schema.object({ bar: schema.string() }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type })).toThrow(/Expected all properties to be semvers/i);
|
||||
|
@ -123,6 +127,23 @@ describe('validateTypeMigrations', () => {
|
|||
`"Migration for type foo for version 8.10.0 registered after switchToModelVersionAt (8.9.0)"`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if a schema is specified for a version superior to switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.9.0',
|
||||
schemas: {
|
||||
'8.10.0': schema.object({ name: schema.string() }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
validate({ type, kibanaVersion: '8.10.0' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Schema for type foo for version 8.10.0 registered after switchToModelVersionAt (8.9.0)"`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if a migration is specified for a version equal to switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
|
@ -139,6 +160,22 @@ describe('validateTypeMigrations', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('throws if a schema is specified for a version equal to switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.9.0',
|
||||
schemas: {
|
||||
'8.9.0': schema.object({ name: schema.string() }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
validate({ type, kibanaVersion: '8.10.0' })
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Schema for type foo for version 8.9.0 registered after switchToModelVersionAt (8.9.0)"`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not throw if a migration is specified for a version inferior to switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
|
@ -150,6 +187,18 @@ describe('validateTypeMigrations', () => {
|
|||
|
||||
expect(() => validate({ type, kibanaVersion: '8.10.0' })).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not throw if a schema is specified for a version inferior to switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.9.0',
|
||||
schemas: {
|
||||
'8.7.0': schema.object({ name: schema.string() }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '8.10.0' })).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -245,6 +294,71 @@ describe('validateTypeMigrations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('modelVersions with schemas', () => {
|
||||
const baseSchema = schema.object({ name: schema.string() }, { unknowns: 'ignore' });
|
||||
|
||||
it('throws if used without specifying switchToModelVersionAt', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
modelVersions: {
|
||||
1: {
|
||||
changes: [],
|
||||
schemas: {
|
||||
forwardCompatibility: baseSchema,
|
||||
create: baseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
name: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Type foo: Using modelVersions requires to specify switchToModelVersionAt"`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not throw passing a model version schema map', () => {
|
||||
const someModelVersionWithSchema = {
|
||||
changes: [],
|
||||
schemas: {
|
||||
forwardCompatibility: baseSchema.extends({}, { unknowns: 'ignore' }),
|
||||
create: baseSchema,
|
||||
},
|
||||
};
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '3.1.0',
|
||||
modelVersions: {
|
||||
'1': someModelVersionWithSchema,
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
name: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).not.toThrow();
|
||||
});
|
||||
|
||||
it('does not throw passing an empty model version schema map', () => {
|
||||
const someModelVersionWithSchema = { changes: [], schemas: {} };
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '3.1.0',
|
||||
modelVersions: {
|
||||
'1': someModelVersionWithSchema,
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('modelVersions mapping additions', () => {
|
||||
it('throws when registering mapping additions not present in the global mappings', () => {
|
||||
const type = createType({
|
||||
|
@ -274,7 +388,7 @@ describe('validateTypeMigrations', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('does not throw when registering mapping additions are present in the global mappings', () => {
|
||||
it('does not throw when registering mapping additions are present in the global mappings with a schema', () => {
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.8.0',
|
||||
|
@ -288,6 +402,7 @@ describe('validateTypeMigrations', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
schemas: {},
|
||||
},
|
||||
'2': {
|
||||
changes: [
|
||||
|
@ -340,6 +455,30 @@ describe('validateTypeMigrations', () => {
|
|||
`"Type foo: mappings added on model versions differs from the global mappings definition: field2.type"`
|
||||
);
|
||||
});
|
||||
|
||||
it('does not throw if a schema is specified for a modelVersion with no changes', () => {
|
||||
const baseSchema = schema.object({ name: schema.string() });
|
||||
const type = createType({
|
||||
name: 'foo',
|
||||
switchToModelVersionAt: '8.10.0',
|
||||
modelVersions: {
|
||||
1: {
|
||||
changes: [],
|
||||
schemas: {
|
||||
forwardCompatibility: baseSchema.extends({}, { unknowns: 'ignore' }),
|
||||
create: baseSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
mappings: {
|
||||
properties: {
|
||||
name: { type: 'text' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => validate({ type, kibanaVersion: '3.2.3' })).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToMultiNamespaceTypeVersion', () => {
|
||||
|
|
|
@ -69,6 +69,23 @@ export function validateTypeMigrations({
|
|||
});
|
||||
}
|
||||
|
||||
if (type.schemas) {
|
||||
const schemaMap = typeof type.schemas === 'object' ? type.schemas : {};
|
||||
assertObject(
|
||||
schemaMap,
|
||||
`Schemas map for type ${type.name} should be an object like { '2.0.0': {schema} }.`
|
||||
);
|
||||
|
||||
Object.entries(schemaMap).forEach(([version, schema]) => {
|
||||
assertValidSemver(kibanaVersion, version, type.name);
|
||||
if (type.switchToModelVersionAt && Semver.gte(version, type.switchToModelVersionAt)) {
|
||||
throw new Error(
|
||||
`Schema for type ${type.name} for version ${version} registered after switchToModelVersionAt (${type.switchToModelVersionAt})`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (type.modelVersions) {
|
||||
const modelVersionMap =
|
||||
typeof type.modelVersions === 'function' ? type.modelVersions() : type.modelVersions ?? {};
|
||||
|
|
|
@ -76,7 +76,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"cases-connector-mappings": "f9d1ac57e484e69506c36a8051e4d61f4a8cfd25",
|
||||
"cases-telemetry": "f219eb7e26772884342487fc9602cfea07b3cedc",
|
||||
"cases-user-actions": "483f10db9b3bd1617948d7032a98b7791bf87414",
|
||||
"cloud-security-posture-settings": "675e47dd958fbce6c70a20baac12af3145e7c0ef",
|
||||
"cloud-security-posture-settings": "e0f61c68bbb5e4cfa46ce8994fa001e417df51ca",
|
||||
"config": "179b3e2bc672626aafce3cf92093a113f456af38",
|
||||
"config-global": "8e8a134a2952df700d7d4ec51abb794bbd4cf6da",
|
||||
"connector_token": "5a9ac29fe9c740eb114e9c40517245c71706b005",
|
||||
|
|
|
@ -16,8 +16,15 @@ export const cspSettings: SavedObjectsType = {
|
|||
indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX,
|
||||
hidden: true,
|
||||
namespaceType: 'agnostic',
|
||||
schemas: {
|
||||
'8.12.0': cspSettingsSchema,
|
||||
modelVersions: {
|
||||
1: {
|
||||
changes: [],
|
||||
schemas: {
|
||||
forwardCompatibility: cspSettingsSchema.extends({}, { unknowns: 'ignore' }),
|
||||
create: cspSettingsSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
schemas: {},
|
||||
mappings: cspSettingsSavedObjectMapping,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue