[Config Service] Ignore unknown config on Serverless (#187006)

This commit is contained in:
Alejandro Fernández Haro 2024-07-01 19:32:22 +02:00 committed by GitHub
parent 1a8a905835
commit 00337de019
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 161 additions and 1354 deletions

View file

@ -7,4 +7,7 @@
*/
export { coreDeprecationProvider } from './src/deprecation';
export { ensureValidConfiguration } from './src/ensure_valid_configuration';
export {
ensureValidConfiguration,
type EnsureValidConfigurationParameters,
} from './src/ensure_valid_configuration';

View file

@ -22,19 +22,19 @@ describe('ensureValidConfiguration', () => {
});
it('returns normally when there is no unused keys and when the config validates', async () => {
await expect(ensureValidConfiguration(configService as any)).resolves.toBeUndefined();
await expect(ensureValidConfiguration(configService)).resolves.toBeUndefined();
expect(configService.validate).toHaveBeenCalledWith(undefined);
expect(configService.validate).toHaveBeenCalledWith({ logDeprecations: true });
});
it('forwards parameters to the `validate` method', async () => {
await expect(
ensureValidConfiguration(configService as any, { logDeprecations: false })
ensureValidConfiguration(configService, { logDeprecations: false })
).resolves.toBeUndefined();
expect(configService.validate).toHaveBeenCalledWith({ logDeprecations: false });
await expect(
ensureValidConfiguration(configService as any, { logDeprecations: true })
ensureValidConfiguration(configService, { logDeprecations: true })
).resolves.toBeUndefined();
expect(configService.validate).toHaveBeenCalledWith({ logDeprecations: true });
});
@ -44,7 +44,7 @@ describe('ensureValidConfiguration', () => {
throw new Error('some message');
});
await expect(ensureValidConfiguration(configService as any)).rejects.toMatchInlineSnapshot(
await expect(ensureValidConfiguration(configService)).rejects.toMatchInlineSnapshot(
`[Error: some message]`
);
});
@ -57,7 +57,7 @@ describe('ensureValidConfiguration', () => {
});
try {
await ensureValidConfiguration(configService as any);
await ensureValidConfiguration(configService);
} catch (e) {
expect(e).toBeInstanceOf(CriticalError);
expect(e.processExitCode).toEqual(78);
@ -67,18 +67,26 @@ describe('ensureValidConfiguration', () => {
it('throws when there are some unused keys', async () => {
configService.getUnusedPaths.mockResolvedValue(['some.key', 'some.other.key']);
await expect(ensureValidConfiguration(configService as any)).rejects.toMatchInlineSnapshot(
await expect(ensureValidConfiguration(configService)).rejects.toMatchInlineSnapshot(
`[Error: Unknown configuration key(s): "some.key", "some.other.key". Check for spelling errors and ensure that expected plugins are installed.]`
);
});
it('does not throw when there are some unused keys and skipUnusedConfigKeysCheck: true', async () => {
configService.getUnusedPaths.mockResolvedValue(['some.key', 'some.other.key']);
await expect(
ensureValidConfiguration(configService, { logDeprecations: true, stripUnknownKeys: true })
).resolves.toBeUndefined();
});
it('throws a `CriticalError` with the correct processExitCode value', async () => {
expect.assertions(2);
configService.getUnusedPaths.mockResolvedValue(['some.key', 'some.other.key']);
try {
await ensureValidConfiguration(configService as any);
await ensureValidConfiguration(configService);
} catch (e) {
expect(e).toBeInstanceOf(CriticalError);
expect(e.processExitCode).toEqual(64);
@ -88,13 +96,13 @@ describe('ensureValidConfiguration', () => {
it('does not throw when all unused keys are included in the ignored paths', async () => {
configService.getUnusedPaths.mockResolvedValue(['dev.someDevKey', 'elastic.apm.enabled']);
await expect(ensureValidConfiguration(configService as any)).resolves.toBeUndefined();
await expect(ensureValidConfiguration(configService)).resolves.toBeUndefined();
});
it('throws when only some keys are included in the ignored paths', async () => {
configService.getUnusedPaths.mockResolvedValue(['dev.someDevKey', 'some.key']);
await expect(ensureValidConfiguration(configService as any)).rejects.toMatchInlineSnapshot(
await expect(ensureValidConfiguration(configService)).rejects.toMatchInlineSnapshot(
`[Error: Unknown configuration key(s): "some.key". Check for spelling errors and ensure that expected plugins are installed.]`
);
});

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { ConfigService, ConfigValidateParameters } from '@kbn/config';
import type { IConfigService, ConfigValidateParameters } from '@kbn/config';
import { CriticalError } from '@kbn/core-base-server-internal';
const ignoredPaths = ['dev.', 'elastic.apm.'];
@ -14,12 +14,32 @@ const ignoredPaths = ['dev.', 'elastic.apm.'];
const invalidConfigExitCode = 78;
const legacyInvalidConfigExitCode = 64;
/**
* Parameters for the helper {@link ensureValidConfiguration}
*
* @private
*/
export interface EnsureValidConfigurationParameters extends ConfigValidateParameters {
/**
* Set to `true` to ignore any unknown keys and discard them from the final validated config object.
*/
stripUnknownKeys?: boolean;
}
/**
* Validate the entire Kibana configuration object, including the detection of extra keys.
* @param configService The {@link IConfigService} instance that has the raw configuration preloaded.
* @param params {@link EnsureValidConfigurationParameters | Options} to enable/disable extra edge-cases.
*
* @private
*/
export async function ensureValidConfiguration(
configService: ConfigService,
params?: ConfigValidateParameters
configService: IConfigService,
params: EnsureValidConfigurationParameters = { logDeprecations: true }
) {
const { stripUnknownKeys, ...validateParams } = params;
try {
await configService.validate(params);
await configService.validate(validateParams);
} catch (e) {
throw new CriticalError(e.message, 'InvalidConfig', invalidConfigExitCode, e);
}
@ -29,7 +49,7 @@ export async function ensureValidConfiguration(
return !ignoredPaths.some((ignoredPath) => unusedPath.startsWith(ignoredPath));
});
if (unusedConfigKeys.length > 0) {
if (unusedConfigKeys.length > 0 && !stripUnknownKeys) {
const message = `Unknown configuration key(s): ${unusedConfigKeys
.map((key) => `"${key}"`)
.join(', ')}. Check for spelling errors and ensure that expected plugins are installed.`;

View file

@ -256,7 +256,7 @@ export class Server {
const environmentSetup = this.environment.setup();
// Configuration could have changed after preboot.
await ensureValidConfiguration(this.configService);
await this.ensureValidConfiguration();
const { uiPlugins, pluginPaths, pluginTree } = this.discoveredPlugins!.standard;
const contextServiceSetup = this.context.setup({
@ -486,6 +486,27 @@ export class Server {
this.userProfile.stop();
}
private async ensureValidConfiguration() {
try {
await ensureValidConfiguration(this.configService);
} catch (validationError) {
if (this.env.packageInfo.buildFlavor !== 'serverless') {
throw validationError;
}
// When running on serverless, we may allow unknown keys, but stripping them from the final config object.
this.configService.setGlobalStripUnknownKeys(true);
await ensureValidConfiguration(this.configService, {
logDeprecations: true,
stripUnknownKeys: true,
});
this.log
.get('config-validation')
.error(
`Strict config validation failed! Extra unknown keys removed in Serverless-compatible mode. Original error: ${validationError}`
);
}
}
private registerCoreContext(coreSetup: InternalCoreSetup) {
coreSetup.http.registerRouteHandlerContext<RequestHandlerContext, 'core'>(
coreId,

View file

@ -29,6 +29,7 @@ const createConfigServiceMock = ({
getDeprecatedConfigPath$: jest.fn(),
addDynamicConfigPaths: jest.fn(),
setDynamicConfigOverrides: jest.fn(),
setGlobalStripUnknownKeys: jest.fn(),
};
mocked.atPath.mockReturnValue(new BehaviorSubject(atPath));

View file

@ -64,6 +64,8 @@ Every schema instance has a `validate` method that is used to perform a validati
* `data: any` - **required**, data to be validated with the schema
* `context: Record<string, any>` - **optional**, object whose properties can be referenced by the [context references](#schemacontextref)
* `namespace: string` - **optional**, arbitrary string that is used to prefix every error message thrown during validation
* `validationOptions: SchemaValidationOptions` - **optional**, global options to modify the default validation behavior
* `stripUnknownKeys: boolean` - **optional**, when `true`, it changes the default `unknowns: 'forbid'` to behave like `unknowns: 'ignore'`. This change of behavior only occurs in schemas without an explicit `unknowns` option. Refer to [`schema.object()`](#schemaobject) for more information about the `unknowns` option.
```typescript
const valueSchema = schema.object({
@ -243,7 +245,7 @@ __Output type:__ `{ [K in keyof TProps]: TypeOf<TProps[K]> } as TObject`
__Options:__
* `defaultValue: TObject | Reference<TObject> | (() => TObject)` - defines a default value, see [Default values](#default-values) section for more details.
* `validate: (value: TObject) => string | void` - defines a custom validator function, see [Custom validation](#custom-validation) section for more details.
* `unknowns: 'allow' | 'ignore' | 'forbid'` - indicates whether unknown object properties should be allowed, ignored, or forbidden. It's `forbid` by default.
* `unknowns: 'allow' | 'ignore' | 'forbid'` - indicates whether unknown object properties and sub-properties should be allowed, ignored, or forbidden. It is `forbid` by default unless the global validation option `stripUnknownKeys` is set to `true` when calling `validate()`. Refer to [the `validate()` API options](#schema-building-blocks) to learn about `stripUnknownKeys`.
__Usage:__
```typescript
@ -255,6 +257,7 @@ const valueSchema = schema.object({
__Notes:__
* Using `unknowns: 'allow'` is discouraged and should only be used in exceptional circumstances. Consider using `schema.recordOf()` instead.
* Bear in mind that specifying `unknowns: 'allow' | 'ignore' | 'forbid'` applies to the entire tree of sub-objects. If you want this option to apply only to the properties in first level, make sure to override this option by setting a new `unknowns` option in the child `schema.object()`s.
* Currently `schema.object()` always has a default value of `{}`, but this may change in the near future. Try to not rely on this behaviour and specify default value explicitly or use `schema.maybe()` if the value is optional.
* `schema.object()` also supports a json string as input if it can be safely parsed using `JSON.parse` and if the resulting value is a plain object.

View file

@ -57,6 +57,7 @@ import {
export type { AnyType, ConditionalType, TypeOf, Props, SchemaStructureEntry, NullableProps };
export { ObjectType, Type };
export type { SchemaValidationOptions } from './src/types';
export { ByteSizeValue } from './src/byte_size_value';
export { SchemaTypeError, ValidationError } from './src/errors';
export { isConfigSchema } from './src/typeguards';

View file

@ -6,8 +6,7 @@
* Side Public License, v 1.
*/
export type { TypeOptions } from './type';
export type { SchemaStructureEntry } from './type';
export type { SchemaStructureEntry, SchemaValidationOptions, TypeOptions } from './type';
export { Type } from './type';
export { AnyType } from './any_type';
export type { ArrayOptions } from './array_type';

View file

@ -338,12 +338,32 @@ test('allow and remove unknown keys when unknowns = `ignore`', () => {
});
});
test('unknowns = `ignore` affects only own keys', () => {
test('unknowns = `ignore` is recursive if no explicit preferences in sub-keys', () => {
const type = schema.object(
{ foo: schema.object({ bar: schema.string() }) },
{ unknowns: 'ignore' }
);
expect(
type.validate({
foo: {
bar: 'bar',
baz: 'baz',
},
})
).toEqual({
foo: {
bar: 'bar',
},
});
});
test('unknowns = `ignore` respects local preferences in sub-keys', () => {
const type = schema.object(
{ foo: schema.object({ bar: schema.string() }, { unknowns: 'forbid' }) },
{ unknowns: 'ignore' }
);
expect(() =>
type.validate({
foo: {
@ -354,7 +374,7 @@ test('unknowns = `ignore` affects only own keys', () => {
).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`);
});
describe('nested unknows', () => {
describe('nested unknowns', () => {
test('allow unknown keys when unknowns = `allow`', () => {
const type = schema.object({
myObj: schema.object({ foo: schema.string({ defaultValue: 'test' }) }, { unknowns: 'allow' }),
@ -427,8 +447,7 @@ describe('nested unknows', () => {
},
});
});
test('unknowns = `ignore` affects only own keys', () => {
test('unknowns = `ignore` is recursive if no explicit preferences in sub-keys', () => {
const type = schema.object({
myObj: schema.object(
{ foo: schema.object({ bar: schema.string() }) },
@ -436,6 +455,32 @@ describe('nested unknows', () => {
),
});
expect(
type.validate({
myObj: {
foo: {
bar: 'bar',
baz: 'baz',
},
},
})
).toEqual({
myObj: {
foo: {
bar: 'bar',
},
},
});
});
test('unknowns = `ignore` respects local preferences in sub-keys', () => {
const type = schema.object({
myObj: schema.object(
{ foo: schema.object({ bar: schema.string() }, { unknowns: 'forbid' }) },
{ unknowns: 'ignore' }
),
});
expect(() =>
type.validate({
myObj: {

View file

@ -87,16 +87,11 @@ export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>>
constructor(props: P, options: ObjectTypeOptions<P> = {}) {
const schemaKeys = {} as Record<string, AnySchema>;
const { unknowns = 'forbid', ...typeOptions } = options;
const { unknowns, ...typeOptions } = options;
for (const [key, value] of Object.entries(props)) {
schemaKeys[key] = value.getSchema();
}
let schema = internals
.object()
.keys(schemaKeys)
.default()
.optional()
.options({ stripUnknown: { objects: unknowns === 'ignore' } });
let schema = internals.object().keys(schemaKeys).default().optional();
// We need to specify the `.unknown` property only when we want to override the default `forbid`
// or it will break `stripUnknown` functionality.
@ -104,6 +99,11 @@ export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>>
schema = schema.unknown(unknowns === 'allow');
}
// Only set stripUnknown if we have an explicit value of `unknowns`
if (unknowns) {
schema = schema.options({ stripUnknown: { objects: unknowns === 'ignore' } });
}
if (options.meta?.id) {
schema = schema.id(options.meta.id);
}

View file

@ -37,6 +37,16 @@ export interface SchemaStructureEntry {
type: string;
}
/**
* Global validation Options to be provided when calling the `schema.validate()` method.
*/
export interface SchemaValidationOptions {
/**
* Remove unknown config keys
*/
stripUnknownKeys?: boolean;
}
/**
* Options for dealing with unknown keys:
* - allow: unknown keys will be permitted
@ -129,10 +139,16 @@ export abstract class Type<V> {
* Validates the provided value against this schema.
* If valid, the resulting output will be returned, otherwise an exception will be thrown.
*/
public validate(value: unknown, context: Record<string, unknown> = {}, namespace?: string): V {
public validate(
value: unknown,
context: Record<string, unknown> = {},
namespace?: string,
validationOptions?: SchemaValidationOptions
): V {
const { value: validatedValue, error } = this.internalSchema.validate(value, {
context,
presence: 'required',
stripUnknown: { objects: validationOptions?.stripUnknownKeys === true },
});
if (error) {

View file

@ -47,6 +47,7 @@ export class ConfigService {
private readonly deprecationLog: Logger;
private readonly docLinks: DocLinks;
private stripUnknownKeys = false;
private validated = false;
private readonly config$: Observable<Config>;
private lastConfig?: Config;
@ -96,6 +97,14 @@ export class ConfigService {
);
}
/**
* Set the global setting for stripUnknownKeys. Useful for running in Serverless-compatible way.
* @param stripUnknownKeys Set to `true` if unknown keys (not explicitly forbidden) should be dropped without failing validation
*/
public setGlobalStripUnknownKeys(stripUnknownKeys: boolean) {
this.stripUnknownKeys = stripUnknownKeys;
}
/**
* Set config schema for a path and performs its validation
*/
@ -139,7 +148,7 @@ export class ConfigService {
public async validate(params: ConfigValidateParameters = { logDeprecations: true }) {
const namespaces = [...this.schemas.keys()];
for (let i = 0; i < namespaces.length; i++) {
await this.getValidatedConfigAtPath$(namespaces[i]).pipe(first()).toPromise();
await firstValueFrom(this.getValidatedConfigAtPath$(namespaces[i]));
}
if (params.logDeprecations) {
@ -313,7 +322,8 @@ export class ConfigService {
serverless: this.env.packageInfo.buildFlavor === 'serverless',
...this.env.packageInfo,
},
`config validation of [${namespace}]`
`config validation of [${namespace}]`,
this.stripUnknownKeys ? { stripUnknownKeys: this.stripUnknownKeys } : {}
);
}

View file

@ -183,11 +183,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -457,11 +452,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -814,11 +804,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"rules": Array [
Object {
"args": Object {
@ -1129,11 +1114,6 @@ Object {
"type": "any",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -1556,11 +1536,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"rules": Array [
Object {
"args": Object {
@ -1784,11 +1759,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -1968,11 +1938,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -1981,11 +1946,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"serviceName": Object {
@ -2049,11 +2009,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -2256,11 +2211,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -2269,11 +2219,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"serviceName": Object {
@ -2379,11 +2324,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -2542,11 +2482,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -2555,11 +2490,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"serviceName": Object {
@ -2665,11 +2595,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -2824,11 +2749,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"metric": Object {
@ -2936,11 +2856,6 @@ Object {
"type": "array",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -3225,11 +3140,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -3302,11 +3212,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -3352,11 +3257,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -3535,11 +3435,6 @@ Object {
"type": "record",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -3712,11 +3607,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -3803,11 +3693,6 @@ Object {
"type": "any",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -4021,11 +3906,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"name": Object {
@ -4184,11 +4064,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"popularity": Object {
@ -4239,11 +4114,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"type": Object {
@ -4339,11 +4209,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -4498,11 +4363,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"popularity": Object {
@ -4619,11 +4479,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -4659,11 +4514,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"type": Object {
@ -4677,11 +4527,6 @@ Object {
"type": "any",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -4796,11 +4641,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"nested": Object {
@ -4830,11 +4670,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -4843,11 +4678,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"type": Object {
@ -5110,11 +4940,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"popularity": Object {
@ -5165,11 +4990,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"type": Object {
@ -5265,11 +5085,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -5424,11 +5239,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"popularity": Object {
@ -5545,11 +5355,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -5585,11 +5390,6 @@ Object {
"x-oas-optional": true,
},
],
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"type": Object {
@ -5603,11 +5403,6 @@ Object {
"type": "any",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -5690,11 +5485,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -5803,11 +5593,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -5858,19 +5643,9 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -7909,11 +7684,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -9928,11 +9698,6 @@ Object {
"type": "string",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
@ -10035,11 +9800,6 @@ Object {
"type": "number",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
"maxBurnRateThreshold": Object {
@ -10102,30 +9862,15 @@ Object {
"type": "number",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
],
"type": "array",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -10255,11 +10000,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -10304,11 +10044,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -10353,11 +10088,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -10377,11 +10107,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -10401,11 +10126,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -10488,11 +10208,6 @@ Object {
"type": "array",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"rules": Array [
Object {
"args": Object {
@ -10644,11 +10359,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;
@ -10731,11 +10441,6 @@ Object {
"type": "array",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"rules": Array [
Object {
"args": Object {
@ -10822,11 +10527,6 @@ Object {
"type": "array",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"rules": Array [
Object {
"args": Object {
@ -10918,11 +10618,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -10967,11 +10662,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -11092,11 +10782,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -11141,11 +10826,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -11190,11 +10870,6 @@ Object {
"type": "boolean",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -11214,11 +10889,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
},
},
@ -11238,11 +10908,6 @@ Object {
"type": "alternatives",
},
},
"preferences": Object {
"stripUnknown": Object {
"objects": false,
},
},
"type": "object",
}
`;