diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.config.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.config.md
index 897e1617486715..12a32b4544abaf 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.config.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.config.md
@@ -9,7 +9,7 @@ A limited set of Elasticsearch configuration entries.
Signature:
```typescript
-readonly config: ElasticsearchConfigPreboot;
+readonly config: Readonly;
```
## Example
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.md
index 0b5847ec889435..bf458004b488bd 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchservicepreboot.md
@@ -15,6 +15,6 @@ export interface ElasticsearchServicePreboot
| Property | Type | Description |
| --- | --- | --- |
-| [config](./kibana-plugin-core-server.elasticsearchservicepreboot.config.md) | ElasticsearchConfigPreboot
| A limited set of Elasticsearch configuration entries. |
+| [config](./kibana-plugin-core-server.elasticsearchservicepreboot.config.md) | Readonly<ElasticsearchConfigPreboot>
| A limited set of Elasticsearch configuration entries. |
| [createClient](./kibana-plugin-core-server.elasticsearchservicepreboot.createclient.md) | (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ICustomClusterClient
| Create application specific Elasticsearch cluster API client with customized config. See [IClusterClient](./kibana-plugin-core-server.iclusterclient.md). |
diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts
index a9e360e733f0da..375c7015b16d79 100644
--- a/src/core/server/elasticsearch/types.ts
+++ b/src/core/server/elasticsearch/types.ts
@@ -31,7 +31,7 @@ export interface ElasticsearchServicePreboot {
* const { hosts, credentialsSpecified } = core.elasticsearch.config;
* ```
*/
- readonly config: ElasticsearchConfigPreboot;
+ readonly config: Readonly;
/**
* Create application specific Elasticsearch cluster API client with customized config. See {@link IClusterClient}.
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 2b265948b6c210..52548c760e30bb 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -1000,7 +1000,7 @@ export interface ElasticsearchConfigPreboot {
// @public (undocumented)
export interface ElasticsearchServicePreboot {
- readonly config: ElasticsearchConfigPreboot;
+ readonly config: Readonly;
readonly createClient: (type: string, clientConfig?: Partial) => ICustomClusterClient;
}
diff --git a/src/core/server/ui_settings/base_ui_settings_client.ts b/src/core/server/ui_settings/base_ui_settings_client.ts
new file mode 100644
index 00000000000000..d16524ef4e3431
--- /dev/null
+++ b/src/core/server/ui_settings/base_ui_settings_client.ts
@@ -0,0 +1,93 @@
+/*
+ * 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 { omit } from 'lodash';
+
+import {
+ IUiSettingsClient,
+ UiSettingsParams,
+ PublicUiSettingsParams,
+ UserProvidedValues,
+} from './types';
+import { Logger } from '../logging';
+
+export interface BaseUiSettingsDefaultsClientOptions {
+ overrides?: Record;
+ defaults?: Record;
+ log: Logger;
+}
+
+/**
+ * Base implementation of the {@link IUiSettingsClient}.
+ */
+export abstract class BaseUiSettingsClient implements IUiSettingsClient {
+ private readonly defaults: NonNullable;
+ private readonly defaultValues: Record;
+ protected readonly overrides: NonNullable;
+ protected readonly log: Logger;
+
+ protected constructor(options: BaseUiSettingsDefaultsClientOptions) {
+ const { defaults = {}, overrides = {}, log } = options;
+ this.log = log;
+ this.overrides = overrides;
+
+ this.defaults = defaults;
+ this.defaultValues = Object.fromEntries(
+ Object.entries(this.defaults).map(([key, { value }]) => [key, value])
+ );
+ }
+
+ getRegistered() {
+ const copiedDefaults: Record = {};
+ for (const [key, value] of Object.entries(this.defaults)) {
+ copiedDefaults[key] = omit(value, 'schema');
+ }
+ return copiedDefaults;
+ }
+
+ async get(key: string): Promise {
+ const all = await this.getAll();
+ return all[key] as T;
+ }
+
+ async getAll() {
+ const result = { ...this.defaultValues };
+
+ const userProvided = await this.getUserProvided();
+ Object.keys(userProvided).forEach((key) => {
+ if (userProvided[key].userValue !== undefined) {
+ result[key] = userProvided[key].userValue;
+ }
+ });
+
+ return Object.freeze(result) as Record;
+ }
+
+ isOverridden(key: string) {
+ return this.overrides.hasOwnProperty(key);
+ }
+
+ isSensitive(key: string): boolean {
+ const definition = this.defaults[key];
+ return !!definition?.sensitive;
+ }
+
+ protected validateKey(key: string, value: unknown) {
+ const definition = this.defaults[key];
+ if (value === null || definition === undefined) return;
+ if (definition.schema) {
+ definition.schema.validate(value, {}, `validation [${key}]`);
+ }
+ }
+
+ abstract getUserProvided(): Promise>>;
+ abstract setMany(changes: Record): Promise;
+ abstract set(key: string, value: any): Promise;
+ abstract remove(key: string): Promise;
+ abstract removeMany(keys: string[]): Promise;
+}
diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts
index 61a4966348e74f..53ed41a3e12be5 100644
--- a/src/core/server/ui_settings/ui_settings_client.ts
+++ b/src/core/server/ui_settings/ui_settings_client.ts
@@ -6,21 +6,20 @@
* Side Public License, v 1.
*/
-import { omit } from 'lodash';
-
import { SavedObjectsErrorHelpers } from '../saved_objects';
import { SavedObjectsClientContract } from '../saved_objects/types';
import { Logger } from '../logging';
import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config';
-import { IUiSettingsClient, UiSettingsParams, PublicUiSettingsParams } from './types';
+import { UiSettingsParams } from './types';
import { CannotOverrideError } from './ui_settings_errors';
import { Cache } from './cache';
+import { BaseUiSettingsClient } from './base_ui_settings_client';
export interface UiSettingsServiceOptions {
type: string;
id: string;
buildNum: number;
- savedObjectsClient?: SavedObjectsClientContract;
+ savedObjectsClient: SavedObjectsClientContract;
overrides?: Record;
defaults?: Record;
log: Logger;
@@ -37,67 +36,22 @@ interface UserProvidedValue {
type UserProvided = Record>;
-function assertSavedObjectsClient(
- savedObjectsClient?: SavedObjectsClientContract
-): asserts savedObjectsClient is SavedObjectsClientContract {
- if (!savedObjectsClient) {
- throw new Error('Saved Objects Client is not available.');
- }
-}
-
-export class UiSettingsClient implements IUiSettingsClient {
+export class UiSettingsClient extends BaseUiSettingsClient {
private readonly type: UiSettingsServiceOptions['type'];
private readonly id: UiSettingsServiceOptions['id'];
private readonly buildNum: UiSettingsServiceOptions['buildNum'];
private readonly savedObjectsClient: UiSettingsServiceOptions['savedObjectsClient'];
- private readonly overrides: NonNullable;
- private readonly defaults: NonNullable;
- private readonly defaultValues: Record;
- private readonly log: Logger;
private readonly cache: Cache;
constructor(options: UiSettingsServiceOptions) {
const { type, id, buildNum, savedObjectsClient, log, defaults = {}, overrides = {} } = options;
+ super({ overrides, defaults, log });
+
this.type = type;
this.id = id;
this.buildNum = buildNum;
this.savedObjectsClient = savedObjectsClient;
- this.overrides = overrides;
- this.log = log;
this.cache = new Cache();
- this.defaults = defaults;
- const defaultValues: Record = {};
- Object.keys(this.defaults).forEach((key) => {
- defaultValues[key] = this.defaults[key].value;
- });
- this.defaultValues = defaultValues;
- }
-
- getRegistered() {
- const copiedDefaults: Record = {};
- for (const [key, value] of Object.entries(this.defaults)) {
- copiedDefaults[key] = omit(value, 'schema');
- }
- return copiedDefaults;
- }
-
- async get(key: string): Promise {
- const all = await this.getAll();
- return all[key] as T;
- }
-
- async getAll() {
- const result = { ...this.defaultValues };
-
- const userProvided = await this.getUserProvided();
- Object.keys(userProvided).forEach((key) => {
- if (userProvided[key].userValue !== undefined) {
- result[key] = userProvided[key].userValue;
- }
- });
-
- Object.freeze(result);
- return result as Record;
}
async getUserProvided(): Promise> {
@@ -142,29 +96,12 @@ export class UiSettingsClient implements IUiSettingsClient {
await this.setMany(changes);
}
- isOverridden(key: string) {
- return this.overrides.hasOwnProperty(key);
- }
-
- isSensitive(key: string): boolean {
- const definition = this.defaults[key];
- return !!definition?.sensitive;
- }
-
private assertUpdateAllowed(key: string) {
if (this.isOverridden(key)) {
throw new CannotOverrideError(`Unable to update "${key}" because it is overridden`);
}
}
- private validateKey(key: string, value: unknown) {
- const definition = this.defaults[key];
- if (value === null || definition === undefined) return;
- if (definition.schema) {
- definition.schema.validate(value, {}, `validation [${key}]`);
- }
- }
-
private onWriteHook(changes: Record) {
for (const key of Object.keys(changes)) {
this.assertUpdateAllowed(key);
@@ -201,7 +138,6 @@ export class UiSettingsClient implements IUiSettingsClient {
changes: Record;
autoCreateOrUpgradeIfMissing?: boolean;
}) {
- assertSavedObjectsClient(this.savedObjectsClient);
try {
await this.savedObjectsClient.update(this.type, this.id, changes);
} catch (error) {
@@ -227,7 +163,6 @@ export class UiSettingsClient implements IUiSettingsClient {
private async read({ autoCreateOrUpgradeIfMissing = true }: ReadOptions = {}): Promise<
Record
> {
- assertSavedObjectsClient(this.savedObjectsClient);
try {
const resp = await this.savedObjectsClient.get>(this.type, this.id);
return resp.attributes;
@@ -257,7 +192,6 @@ export class UiSettingsClient implements IUiSettingsClient {
}
private isIgnorableError(error: Error) {
- assertSavedObjectsClient(this.savedObjectsClient);
const { isForbiddenError, isEsUnavailableError } = this.savedObjectsClient.errors;
return isForbiddenError(error) || isEsUnavailableError(error);
diff --git a/src/core/server/ui_settings/ui_settings_defaults_client.test.ts b/src/core/server/ui_settings/ui_settings_defaults_client.test.ts
new file mode 100644
index 00000000000000..f3e779034ab4bc
--- /dev/null
+++ b/src/core/server/ui_settings/ui_settings_defaults_client.test.ts
@@ -0,0 +1,188 @@
+/*
+ * 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 Chance from 'chance';
+import { schema } from '@kbn/config-schema';
+
+import { loggingSystemMock } from '../logging/logging_system.mock';
+
+import { UiSettingsDefaultsClient } from './ui_settings_defaults_client';
+
+const logger = loggingSystemMock.create().get();
+
+const chance = new Chance();
+
+describe('ui settings defaults', () => {
+ afterEach(() => jest.clearAllMocks());
+
+ describe('#getUserProvided()', () => {
+ it('only returns overridden values', async () => {
+ const defaults = { foo: { schema: schema.string(), value: 'default foo' } };
+ const overrides = { bar: 'overridden bar', baz: null };
+
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, overrides, log: logger });
+ await expect(uiSettings.getUserProvided()).resolves.toStrictEqual({
+ bar: { userValue: 'overridden bar', isOverridden: true },
+ baz: { isOverridden: true },
+ });
+ });
+ });
+
+ describe('#getAll()', () => {
+ it('returns defaults and overridden values', async () => {
+ const defaults = {
+ foo: { schema: schema.string(), value: 'default foo' },
+ bar: { schema: schema.string() },
+ baz: { schema: schema.string(), value: 'default baz' },
+ };
+ const overrides = { foo: 'overridden foo', zab: 'overridden zab', baz: null };
+
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, overrides, log: logger });
+
+ await expect(uiSettings.getAll()).resolves.toStrictEqual({
+ foo: 'overridden foo',
+ bar: undefined,
+ baz: 'default baz',
+ zab: 'overridden zab',
+ });
+ });
+
+ it('throws if mutates the result of getAll()', async () => {
+ const defaults = {
+ foo: { schema: schema.string(), value: 'default foo' },
+ };
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, log: logger });
+ const result = await uiSettings.getAll();
+
+ expect(() => {
+ result.foo = 'bar';
+ }).toThrow();
+ });
+ });
+
+ describe('#get()', () => {
+ it('returns the overridden value for an overridden key', async () => {
+ const defaults = {
+ foo: { schema: schema.string(), value: 'default foo' },
+ };
+ const overrides = { foo: 'overridden foo' };
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, overrides, log: logger });
+
+ await expect(uiSettings.get('foo')).resolves.toBe('overridden foo');
+ });
+
+ it('returns the default value for an override with value null', async () => {
+ const defaults = {
+ foo: { schema: schema.string(), value: 'default foo' },
+ };
+ const overrides = { foo: null };
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, overrides, log: logger });
+
+ await expect(uiSettings.get('foo')).resolves.toBe('default foo');
+ });
+
+ it('returns the default value if there is no override', async () => {
+ const defaults = {
+ foo: { schema: schema.string(), value: 'default foo' },
+ };
+ const overrides = {};
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, overrides, log: logger });
+
+ await expect(uiSettings.get('foo')).resolves.toBe('default foo');
+ });
+ });
+
+ describe('#setMany()', () => {
+ it('does not throw', async () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ await expect(uiSettings.setMany()).resolves.not.toThrow();
+ });
+ });
+
+ describe('#set()', () => {
+ it('does not throw', async () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ await expect(uiSettings.set()).resolves.not.toThrow();
+ });
+ });
+
+ describe('#remove()', () => {
+ it('does not throw', async () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ await expect(uiSettings.remove()).resolves.not.toThrow();
+ });
+ });
+
+ describe('#removeMany()', () => {
+ it('does not throw', async () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ await expect(uiSettings.removeMany()).resolves.not.toThrow();
+ });
+ });
+
+ describe('#getRegistered()', () => {
+ it('returns the registered settings passed to the constructor and does not leak validation schema outside', () => {
+ const value = chance.word();
+ const defaults = { key: { schema: schema.string(), value } };
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, log: logger });
+ expect(uiSettings.getRegistered()).toStrictEqual({ key: { value } });
+ });
+ });
+
+ describe('#isSensitive()', () => {
+ it('returns false if sensitive config is not set', () => {
+ const defaults = { foo: { schema: schema.string(), value: '1' } };
+
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, log: logger });
+ expect(uiSettings.isSensitive('foo')).toBe(false);
+ });
+
+ it('returns false if key is not in the settings', () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ expect(uiSettings.isSensitive('baz')).toBe(false);
+ });
+
+ it('returns true if `sensitive` is set', () => {
+ const defaults = { foo: { schema: schema.string(), sensitive: true, value: '1' } };
+
+ const uiSettings = new UiSettingsDefaultsClient({ defaults, log: logger });
+ expect(uiSettings.isSensitive('foo')).toBe(true);
+ });
+ });
+
+ describe('#isOverridden()', () => {
+ it('returns false if no overrides defined', () => {
+ const uiSettings = new UiSettingsDefaultsClient({ log: logger });
+ expect(uiSettings.isOverridden('foo')).toBe(false);
+ });
+
+ it('returns false if overrides defined but key is not included', () => {
+ const uiSettings = new UiSettingsDefaultsClient({
+ overrides: { foo: true, bar: true },
+ log: logger,
+ });
+ expect(uiSettings.isOverridden('baz')).toBe(false);
+ });
+
+ it('returns false for object prototype properties', () => {
+ const uiSettings = new UiSettingsDefaultsClient({
+ overrides: { foo: true, bar: true },
+ log: logger,
+ });
+ expect(uiSettings.isOverridden('hasOwnProperty')).toBe(false);
+ });
+
+ it('returns true if overrides defined and key is overridden', () => {
+ const uiSettings = new UiSettingsDefaultsClient({
+ overrides: { foo: true, bar: true },
+ log: logger,
+ });
+ expect(uiSettings.isOverridden('bar')).toBe(true);
+ });
+ });
+});
diff --git a/src/core/server/ui_settings/ui_settings_defaults_client.ts b/src/core/server/ui_settings/ui_settings_defaults_client.ts
new file mode 100644
index 00000000000000..fad90b9b0eaeed
--- /dev/null
+++ b/src/core/server/ui_settings/ui_settings_defaults_client.ts
@@ -0,0 +1,58 @@
+/*
+ * 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 { UiSettingsParams, UserProvidedValues } from './types';
+import { Logger } from '../logging';
+import { BaseUiSettingsClient } from './base_ui_settings_client';
+
+export interface UiSettingsDefaultsClientOptions {
+ overrides?: Record;
+ defaults?: Record;
+ log: Logger;
+}
+
+/**
+ * Implementation of the {@link IUiSettingsClient} that only gives a read-only access to the default UI Settings values and any overrides.
+ */
+export class UiSettingsDefaultsClient extends BaseUiSettingsClient {
+ private readonly userProvided: Record>;
+
+ constructor(options: UiSettingsDefaultsClientOptions) {
+ super(options);
+
+ // The only "userProvided" settings `UiSettingsDefaultsClient` is aware about are explicit overrides.
+ this.userProvided = Object.fromEntries(
+ Object.entries(this.overrides).map(([key, value]) => [
+ key,
+ // Dropping the userValue if override is null
+ value === null ? { isOverridden: true } : { isOverridden: true, userValue: value },
+ ])
+ );
+ }
+
+ async getUserProvided(): Promise>> {
+ return this.userProvided as Record>;
+ }
+
+ // Any mutating operations are not supported by default UI settings.
+ async setMany() {
+ this.log.warn('`setMany` operation is not supported.');
+ }
+
+ async set() {
+ this.log.warn('`set` operation is not supported.');
+ }
+
+ async remove() {
+ this.log.warn('`remove` operation is not supported.');
+ }
+
+ async removeMany() {
+ this.log.warn('`removeMany` operation is not supported.');
+ }
+}
diff --git a/src/core/server/ui_settings/ui_settings_service.test.mock.ts b/src/core/server/ui_settings/ui_settings_service.test.mock.ts
index afe8b401877bca..aae25563c2b887 100644
--- a/src/core/server/ui_settings/ui_settings_service.test.mock.ts
+++ b/src/core/server/ui_settings/ui_settings_service.test.mock.ts
@@ -11,6 +11,11 @@ jest.doMock('./ui_settings_client', () => ({
UiSettingsClient: MockUiSettingsClientConstructor,
}));
+export const MockUiSettingsDefaultsClientConstructor = jest.fn();
+jest.doMock('./ui_settings_defaults_client', () => ({
+ UiSettingsDefaultsClient: MockUiSettingsDefaultsClientConstructor,
+}));
+
export const getCoreSettingsMock = jest.fn();
jest.doMock('./settings', () => ({
getCoreSettings: getCoreSettingsMock,
diff --git a/src/core/server/ui_settings/ui_settings_service.test.ts b/src/core/server/ui_settings/ui_settings_service.test.ts
index 7f2423c9798a7e..b829f62d2263d7 100644
--- a/src/core/server/ui_settings/ui_settings_service.test.ts
+++ b/src/core/server/ui_settings/ui_settings_service.test.ts
@@ -11,6 +11,7 @@ import { schema } from '@kbn/config-schema';
import {
MockUiSettingsClientConstructor,
+ MockUiSettingsDefaultsClientConstructor,
getCoreSettingsMock,
} from './ui_settings_service.test.mock';
import { UiSettingsService, SetupDeps } from './ui_settings_service';
@@ -19,7 +20,7 @@ import { savedObjectsClientMock } from '../mocks';
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
import { mockCoreContext } from '../core_context.mock';
import { uiSettingsType } from './saved_objects';
-import { UiSettingsClient } from './ui_settings_client';
+import { UiSettingsDefaultsClient } from './ui_settings_defaults_client';
const overrides = {
overrideBaz: 'baz',
@@ -65,11 +66,11 @@ describe('uiSettings', () => {
const { createDefaultsClient } = await service.preboot();
const client = createDefaultsClient();
- expect(client).toBeInstanceOf(UiSettingsClient);
+ expect(client).toBeInstanceOf(UiSettingsDefaultsClient);
- expect(MockUiSettingsClientConstructor).toBeCalledTimes(1);
- const [[constructorArgs]] = MockUiSettingsClientConstructor.mock.calls;
- expect(constructorArgs).toMatchObject({ type: 'config', overrides, defaults: {} });
+ expect(MockUiSettingsDefaultsClientConstructor).toBeCalledTimes(1);
+ const [[constructorArgs]] = MockUiSettingsDefaultsClientConstructor.mock.calls;
+ expect(constructorArgs).toMatchObject({ overrides, defaults: {} });
expect(constructorArgs.overrides).toBe(overrides);
});
});
diff --git a/src/core/server/ui_settings/ui_settings_service.ts b/src/core/server/ui_settings/ui_settings_service.ts
index 5d7073487a72b5..d011b6e21907fe 100644
--- a/src/core/server/ui_settings/ui_settings_service.ts
+++ b/src/core/server/ui_settings/ui_settings_service.ts
@@ -27,6 +27,7 @@ import {
import { uiSettingsType } from './saved_objects';
import { registerRoutes } from './routes';
import { getCoreSettings } from './settings';
+import { UiSettingsDefaultsClient } from './ui_settings_defaults_client';
export interface SetupDeps {
http: InternalHttpServiceSetup;
@@ -56,16 +57,12 @@ export class UiSettingsService
this.register(getCoreSettings({ isDist: this.isDist }));
- const { version, buildNum } = this.coreContext.env.packageInfo;
return {
createDefaultsClient: () =>
- new UiSettingsClient({
- type: 'config',
- id: version,
- buildNum,
+ new UiSettingsDefaultsClient({
defaults: mapToObject(this.uiSettingsDefaults),
overrides: this.overrides,
- log: this.log,
+ log: this.log.get('core defaults'),
}),
};
}