Skip to content

Commit

Permalink
Add applyDefaults option to schema.object
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdover committed Feb 4, 2020
1 parent 4e1d124 commit 81cdfde
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 170 deletions.
9 changes: 9 additions & 0 deletions packages/kbn-config-schema/src/types/object_type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,12 @@ test('does not allow unknown keys when allowUnknowns = false', () => {
})
).toThrowErrorMatchingSnapshot();
});

test('applyDefaults', () => {
const type = schema.object(
{ foo: schema.string({ defaultValue: 'bar' }) },
{ applyDefaults: true }
);

expect(type.validate({})).toEqual({ foo: 'bar' });
});
7 changes: 6 additions & 1 deletion packages/kbn-config-schema/src/types/object_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type ObjectTypeOptions<P extends Props = any> = TypeOptions<
{ [K in keyof P]: TypeOf<P[K]> }
> & {
allowUnknowns?: boolean;
applyDefaults?: boolean;
};

export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>> {
Expand All @@ -45,11 +46,15 @@ export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>>
schemaKeys[key] = value.getSchema();
}
const { allowUnknowns, ...typeOptions } = options;
const schema = internals
let schema = internals
.object()
.keys(schemaKeys)
.unknown(Boolean(allowUnknowns));

if (options.applyDefaults) {
schema = schema.default().optional();
}

super(schema, typeOptions);
this.props = schemaKeys;
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/server/config/config_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,21 +391,21 @@ test('allows plugins to specify "enabled" flag via validation schema', async ()

await configService.setSchema(
'foo',
schema.object({ enabled: schema.boolean({ defaultValue: false }) })
schema.object({ enabled: schema.boolean({ defaultValue: false }) }, { applyDefaults: true })
);

expect(await configService.isEnabledAtPath('foo')).toBe(false);

await configService.setSchema(
'bar',
schema.object({ enabled: schema.boolean({ defaultValue: true }) })
schema.object({ enabled: schema.boolean({ defaultValue: true }) }, { applyDefaults: true })
);

expect(await configService.isEnabledAtPath('bar')).toBe(true);

await configService.setSchema(
'baz',
schema.object({ different: schema.boolean({ defaultValue: true }) })
schema.object({ different: schema.boolean({ defaultValue: true }) }, { applyDefaults: true })
);

expect(await configService.isEnabledAtPath('baz')).toBe(true);
Expand Down
25 changes: 14 additions & 11 deletions src/core/server/csp/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,18 @@ export const config = {
// TODO: Move this to server.csp using config deprecations
// ? https://github.com/elastic/kibana/pull/52251
path: 'csp',
schema: schema.object({
rules: schema.arrayOf(schema.string(), {
defaultValue: [
`script-src 'unsafe-eval' 'self'`,
`worker-src blob: 'self'`,
`style-src 'unsafe-inline' 'self'`,
],
}),
strict: schema.boolean({ defaultValue: true }),
warnLegacyBrowsers: schema.boolean({ defaultValue: true }),
}),
schema: schema.object(
{
rules: schema.arrayOf(schema.string(), {
defaultValue: [
`script-src 'unsafe-eval' 'self'`,
`worker-src blob: 'self'`,
`style-src 'unsafe-inline' 'self'`,
],
}),
strict: schema.boolean({ defaultValue: true }),
warnLegacyBrowsers: schema.boolean({ defaultValue: true }),
},
{ applyDefaults: true }
),
};
177 changes: 95 additions & 82 deletions src/core/server/elasticsearch/elasticsearch_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,92 +31,105 @@ export const DEFAULT_API_VERSION = 'master';
export type ElasticsearchConfigType = TypeOf<typeof configSchema>;
type SslConfigSchema = ElasticsearchConfigType['ssl'];

const configSchema = schema.object({
sniffOnStart: schema.boolean({ defaultValue: false }),
sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], {
defaultValue: false,
}),
sniffOnConnectionFault: schema.boolean({ defaultValue: false }),
hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], {
defaultValue: 'http://localhost:9200',
}),
preserveHost: schema.boolean({ defaultValue: true }),
username: schema.maybe(
schema.conditional(
schema.contextRef('dist'),
false,
schema.string({
const configSchema = schema.object(
{
sniffOnStart: schema.boolean({ defaultValue: false }),
sniffInterval: schema.oneOf([schema.duration(), schema.literal(false)], {
defaultValue: false,
}),
sniffOnConnectionFault: schema.boolean({ defaultValue: false }),
hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], {
defaultValue: 'http://localhost:9200',
}),
preserveHost: schema.boolean({ defaultValue: true }),
username: schema.maybe(
schema.conditional(
schema.contextRef('dist'),
false,
schema.string({
validate: rawConfig => {
if (rawConfig === 'elastic') {
return (
'value of "elastic" is forbidden. This is a superuser account that can obfuscate ' +
'privilege-related issues. You should use the "kibana" user instead.'
);
}
},
}),
schema.string()
)
),
password: schema.maybe(schema.string()),
requestHeadersWhitelist: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: ['authorization'],
}),
customHeaders: schema.recordOf(schema.string(), schema.string(), { defaultValue: {} }),
shardTimeout: schema.duration({ defaultValue: '30s' }),
requestTimeout: schema.duration({ defaultValue: '30s' }),
pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }),
startupTimeout: schema.duration({ defaultValue: '5s' }),
logQueries: schema.boolean({ defaultValue: false }),
ssl: schema.object(
{
verificationMode: schema.oneOf(
[schema.literal('none'), schema.literal('certificate'), schema.literal('full')],
{ defaultValue: 'full' }
),
certificateAuthorities: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.string(), { minSize: 1 })])
),
certificate: schema.maybe(schema.string()),
key: schema.maybe(schema.string()),
keyPassphrase: schema.maybe(schema.string()),
keystore: schema.object(
{
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
},
{ applyDefaults: true }
),
truststore: schema.object(
{
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
},
{ applyDefaults: true }
),
alwaysPresentCertificate: schema.boolean({ defaultValue: false }),
},
{
applyDefaults: true,
validate: rawConfig => {
if (rawConfig === 'elastic') {
return (
'value of "elastic" is forbidden. This is a superuser account that can obfuscate ' +
'privilege-related issues. You should use the "kibana" user instead.'
);
if (rawConfig.key && rawConfig.keystore.path) {
return 'cannot use [key] when [keystore.path] is specified';
}
if (rawConfig.certificate && rawConfig.keystore.path) {
return 'cannot use [certificate] when [keystore.path] is specified';
}
},
}
),
apiVersion: schema.string({ defaultValue: DEFAULT_API_VERSION }),
healthCheck: schema.object(
{ delay: schema.duration({ defaultValue: 2500 }) },
{ applyDefaults: true }
),
ignoreVersionMismatch: schema.conditional(
schema.contextRef('dev'),
false,
schema.boolean({
validate: rawValue => {
if (rawValue === true) {
return '"ignoreVersionMismatch" can only be set to true in development mode';
}
},
defaultValue: false,
}),
schema.string()
)
),
password: schema.maybe(schema.string()),
requestHeadersWhitelist: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], {
defaultValue: ['authorization'],
}),
customHeaders: schema.recordOf(schema.string(), schema.string(), { defaultValue: {} }),
shardTimeout: schema.duration({ defaultValue: '30s' }),
requestTimeout: schema.duration({ defaultValue: '30s' }),
pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }),
startupTimeout: schema.duration({ defaultValue: '5s' }),
logQueries: schema.boolean({ defaultValue: false }),
ssl: schema.object(
{
verificationMode: schema.oneOf(
[schema.literal('none'), schema.literal('certificate'), schema.literal('full')],
{ defaultValue: 'full' }
),
certificateAuthorities: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.string(), { minSize: 1 })])
),
certificate: schema.maybe(schema.string()),
key: schema.maybe(schema.string()),
keyPassphrase: schema.maybe(schema.string()),
keystore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
truststore: schema.object({
path: schema.maybe(schema.string()),
password: schema.maybe(schema.string()),
}),
alwaysPresentCertificate: schema.boolean({ defaultValue: false }),
},
{
validate: rawConfig => {
if (rawConfig.key && rawConfig.keystore.path) {
return 'cannot use [key] when [keystore.path] is specified';
}
if (rawConfig.certificate && rawConfig.keystore.path) {
return 'cannot use [certificate] when [keystore.path] is specified';
}
},
}
),
apiVersion: schema.string({ defaultValue: DEFAULT_API_VERSION }),
healthCheck: schema.object({ delay: schema.duration({ defaultValue: 2500 }) }),
ignoreVersionMismatch: schema.conditional(
schema.contextRef('dev'),
false,
schema.boolean({
validate: rawValue => {
if (rawValue === true) {
return '"ignoreVersionMismatch" can only be set to true in development mode';
}
},
defaultValue: false,
}),
schema.boolean({ defaultValue: false })
),
});
schema.boolean({ defaultValue: false })
),
},
{ applyDefaults: true }
);

const deprecations: ConfigDeprecationProvider = () => [
(settings, fromPath, log) => {
Expand Down
51 changes: 29 additions & 22 deletions src/core/server/http/http_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ export const config = {
true,
schema.object(
{
origin: schema.arrayOf(schema.string()),
origin: schema.arrayOf(schema.string(), {
defaultValue: ['*://localhost:9876'], // karma test server
}),
},
{
defaultValue: {
origin: ['*://localhost:9876'], // karma test server
},
applyDefaults: true,
}
),
schema.boolean({ defaultValue: false })
Expand All @@ -78,31 +78,38 @@ export const config = {
socketTimeout: schema.number({
defaultValue: 120000,
}),
compression: schema.object({
enabled: schema.boolean({ defaultValue: true }),
referrerWhitelist: schema.maybe(
schema.arrayOf(
schema.string({
hostname: true,
}),
{ minSize: 1 }
)
),
}),
compression: schema.object(
{
enabled: schema.boolean({ defaultValue: true }),
referrerWhitelist: schema.maybe(
schema.arrayOf(
schema.string({
hostname: true,
}),
{ minSize: 1 }
)
),
},
{ applyDefaults: true }
),
uuid: schema.maybe(
schema.string({
validate: match(uuidRegexp, 'must be a valid uuid'),
})
),
xsrf: schema.object({
disableProtection: schema.boolean({ defaultValue: false }),
whitelist: schema.arrayOf(
schema.string({ validate: match(/^\//, 'must start with a slash') }),
{ defaultValue: [] }
),
}),
xsrf: schema.object(
{
disableProtection: schema.boolean({ defaultValue: false }),
whitelist: schema.arrayOf(
schema.string({ validate: match(/^\//, 'must start with a slash') }),
{ defaultValue: [] }
),
},
{ applyDefaults: true }
),
},
{
applyDefaults: true,
validate: rawConfig => {
if (!rawConfig.basePath && rawConfig.rewriteBasePath) {
return 'cannot use [rewriteBasePath] when [basePath] is not specified';
Expand Down
9 changes: 6 additions & 3 deletions src/core/server/http/router/validator/validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ describe('Router validator', () => {

it('should validate and infer the type from a config-schema ObjectType', () => {
const schemaValidation = RouteValidator.from({
params: schema.object({
foo: schema.string(),
}),
params: schema.object(
{
foo: schema.string(),
},
{ applyDefaults: true }
),
});

expect(schemaValidation.getParams({ foo: 'bar' })).toStrictEqual({ foo: 'bar' });
Expand Down
Loading

0 comments on commit 81cdfde

Please sign in to comment.