Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per User Dark Mode Preference #151507

Merged
merged 69 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
de37d80
v1 tracer
kc13greiner Feb 13, 2023
105cb80
Adding comments and clean-up
kc13greiner Feb 16, 2023
fccecf4
Removing unused console log
kc13greiner Feb 16, 2023
b8bdc52
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Feb 16, 2023
d64e703
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 16, 2023
3f9a94e
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Feb 16, 2023
93dc347
Circular reference?
kc13greiner Feb 16, 2023
9917e06
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Feb 16, 2023
f77fe84
Comment updates and remove interface implementation in search of circ…
kc13greiner Feb 16, 2023
859a52c
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Feb 16, 2023
223b044
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 16, 2023
21d57b8
Moving types around and renaming
kc13greiner Feb 23, 2023
f6e5bc0
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 23, 2023
00a2487
Unnecessary code removal and using import type notation
kc13greiner Feb 23, 2023
46f328c
Reverting unnecessary comment/spacing fixes. Fixing own commments
kc13greiner Feb 23, 2023
5debac8
Adding logging, changing how request is added, and pushing down logic…
kc13greiner Feb 27, 2023
3ff44ba
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 27, 2023
641ae67
Removing sec plugin references from files
kc13greiner Feb 27, 2023
50dd1a5
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 27, 2023
f13157b
Adding form components for theme on UserProfile
kc13greiner Feb 28, 2023
c420176
Fixing form field
kc13greiner Feb 28, 2023
6e2db48
Adding reload toast when Theme is changed
kc13greiner Feb 28, 2023
871bfb8
Cleaning up comments
kc13greiner Feb 28, 2023
ad9c51f
Reverting unnecessary change
kc13greiner Feb 28, 2023
2dd9bf9
One last comment update
kc13greiner Feb 28, 2023
300b6e8
Code review feedback
kc13greiner Mar 10, 2023
9a14abf
Unit tests for UiSettingsService, RenderingService, UiSettingsUserClient
kc13greiner Mar 13, 2023
e989532
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Mar 13, 2023
e818183
Fixing missing types
kc13greiner Mar 13, 2023
7698836
Fixing type checks
kc13greiner Mar 22, 2023
26bf454
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Mar 23, 2023
3dd2db6
matching snapshots
kc13greiner Mar 23, 2023
25deb64
Adding `user` type, but hiding/disabling it
kc13greiner Mar 23, 2023
51b1c70
Adding tests and fixing ts-ignore
kc13greiner Mar 27, 2023
ba7b5e7
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Mar 27, 2023
294379c
Adding test to verify that the reload toast shows up when darkmode is…
kc13greiner Mar 27, 2023
2868c4c
Adding some js docs
kc13greiner Mar 27, 2023
19dc191
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Mar 27, 2023
d287b61
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 19, 2023
b0a3c96
PR Feedback: refactor UISettingsClient into Core UserSettingsService
kc13greiner Apr 19, 2023
34667b8
Removing merge conflict
kc13greiner Apr 19, 2023
29ee472
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 19, 2023
794aaaf
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Apr 19, 2023
eb2a645
adding userSettings to mock
kc13greiner Apr 19, 2023
b2538bf
Removing remaining references to user ui client
kc13greiner Apr 19, 2023
be9799c
removing old uiSettings user references
kc13greiner Apr 19, 2023
6727ec9
PR Feedback changes
kc13greiner Apr 21, 2023
8b11cc6
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 21, 2023
bddbe43
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Apr 21, 2023
3b67a10
Removing legacy reference
kc13greiner Apr 21, 2023
5595551
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 21, 2023
ab6009b
removing old import
kc13greiner Apr 21, 2023
af580d0
Fixing import
kc13greiner Apr 21, 2023
bf2de5f
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Apr 21, 2023
a5b67ea
Update x-pack/plugins/security/public/account_management/user_profile…
kc13greiner Apr 24, 2023
f8793cf
Update x-pack/plugins/security/public/account_management/user_profile…
kc13greiner Apr 24, 2023
ded0e8b
Update x-pack/plugins/security/public/account_management/user_profile…
kc13greiner Apr 24, 2023
32b04fc
Update x-pack/plugins/security/public/account_management/user_profile…
kc13greiner Apr 24, 2023
498c9c0
Update x-pack/plugins/security/public/account_management/user_profile…
kc13greiner Apr 24, 2023
3b5ed27
Changing button order and form control order based on PR feedback
kc13greiner Apr 24, 2023
ff0a571
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 24, 2023
026d562
PR feedback on messaging
kc13greiner Apr 25, 2023
0b9c993
Providing UserSettingsServiceSetup to the bootstrap_renderer so the p…
kc13greiner Apr 25, 2023
ca952f3
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 25, 2023
c076fcd
PR feedback
kc13greiner Apr 25, 2023
084ed22
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 25, 2023
0aa3a56
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 25, 2023
0a4cb6d
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 25, 2023
f035146
Merge branch 'main' into feature/darkmode_discovery
kc13greiner Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export class CoreRouteHandlerContext implements CoreRequestHandlerContext {
this.savedObjects = new CoreSavedObjectsRouteHandlerContext(coreStart.savedObjects, request);
this.uiSettings = new CoreUiSettingsRouteHandlerContext(
coreStart.uiSettings,
this.savedObjects
this.savedObjects,
request
);
this.deprecations = new CoreDeprecationsRouteHandlerContext(
coreStart.deprecations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ export function createPluginStartContext<TPlugin, TPluginDependencies>(
uiSettings: {
asScopedToClient: deps.uiSettings.asScopedToClient,
globalAsScopedToClient: deps.uiSettings.globalAsScopedToClient,
userAsScopedToClient: deps.uiSettings.userAsScopedToClient,
setUserProfileSettingsClientFactoryProvider:
deps.uiSettings.setUserProfileSettingsClientFactoryProvider,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That kind of delegation / setter APIs should be on the setup contract of the service/plugin. (and not being able to do so is usually a code smell by itself)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should be able to move it to the setup- I will investigate!

},
coreUsageData: deps.coreUsageData,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type { ThemeVersion } from '@kbn/ui-shared-deps-npm';

import type { CoreContext } from '@kbn/core-base-server-internal';
import type { KibanaRequest, HttpAuth } from '@kbn/core-http-server';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-server';
import type { IUiSettingsClient, IUserUiSettingsClient } from '@kbn/core-ui-settings-server';
import type { UiPlugins } from '@kbn/core-plugins-base-server-internal';
import { CustomBranding } from '@kbn/core-custom-branding-common';
import { Template } from './views';
Expand Down Expand Up @@ -89,6 +89,7 @@ export class RenderingService {
uiSettings: {
client: IUiSettingsClient;
globalClient: IUiSettingsClient;
userClient: IUserUiSettingsClient;
},
{ isAnonymousPage = false, vars, includeExposedConfigKeys }: IRenderOptions = {}
) {
Expand All @@ -101,6 +102,7 @@ export class RenderingService {
const buildNum = env.packageInfo.buildNum;
const basePath = http.basePath.get(request);
const { serverBasePath, publicBaseUrl } = http.basePath;

const settings = {
defaults: uiSettings.client?.getRegistered() ?? {},
user: isAnonymousPage ? {} : await uiSettings.client?.getUserProvided(),
Expand All @@ -109,6 +111,11 @@ export class RenderingService {
defaults: uiSettings.globalClient?.getRegistered() ?? {},
user: isAnonymousPage ? {} : await uiSettings.globalClient?.getUserProvided(),
};
const userSettings = {
defaults: {},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to future proof this area of the code, I'd fetch uiSettings.userClient?.getRegistered() ?? {}.

And let the uiSettings.userClient service handle the specifics (if it's not expected to have any registered settings, it should just return {})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit!

user: isAnonymousPage ? {} : await uiSettings.userClient?.getUserProfileSettings(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: silly optimization: fetch the 3 levels concurrently.

IMO page load should improve.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in latest commit - let me know if that is what you meant!

};

let clusterInfo = {};
let branding: CustomBranding = {};
try {
Expand All @@ -129,7 +136,15 @@ export class RenderingService {
// swallow error
}

const darkMode = getSettingValue('theme:darkMode', settings, Boolean);
// If the user profile switch has been toggled, the value takes precedence
const userTheme: string = userSettings.user.darkMode;
let darkMode;
if (userTheme) {
darkMode = userTheme === 'dark';
} else {
darkMode = getSettingValue('theme:darkMode', settings, Boolean);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: as we grow the 3-level settings (Global - Space - User), I'd start considering a common API to fetch the value from any level, taking into account the overrides.

If that's our end-goal (not sure if it is), I'd try our best to name the settings the same way.

WDYT?

Copy link
Contributor

@majagrubic majagrubic Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fetching of user settings shouldn't happen inside the rendering service. There should be a level of abstraction between the rendering service and the settings. I agree with @afharo that we could look into abstracting this in a common way, like creating some sort of configurable, generic service; but if you want to keep it simple for now, have a look at what customBrandingService is doing. The idea would be to have something like

{ themingService } = renderOptions;
const darkTheme = themingService.getThemeForUser(user, request);

and that service would then fetch the value from the settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree a generic client to abstract out the settings would be great as we think about the next phase of UserSettings (or settings in general). Im hesitant to over architect this at the moment based on other priorities.

For now would it be alright to leave the individual clients?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's fine


const themeVersion: ThemeVersion = 'v8';

const stylesheetPaths = getStylesheetPaths({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,4 @@ export interface UserProvidedValues<T = any> {
/**
* Denotes the scope of the setting
*/
export type UiSettingsScope = 'namespace' | 'global';
export type UiSettingsScope = 'namespace' | 'global' | 'user';
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

export { UiSettingsClient } from './ui_settings_client';
export { UiSettingsUserClient } from './ui_settings_user_client';
export { UiSettingsGlobalClient } from './ui_settings_global_client';
export { UiSettingsClientFactory } from './ui_settings_client_factory';
export { UiSettingsDefaultsClient } from './ui_settings_defaults_client';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
* Side Public License, v 1.
*/

import { KibanaRequest } from '@kbn/core-http-server';
import type { UiSettingsServiceOptions } from '../..';
import { UiSettingsClient } from './ui_settings_client';
import { UiSettingsGlobalClient } from './ui_settings_global_client';
import { UiSettingsUserClient } from './ui_settings_user_client';

export class UiSettingsClientFactory {
public static create = (options: UiSettingsServiceOptions) => {
public static create = (options: UiSettingsServiceOptions, request?: KibanaRequest) => {
switch (options.type) {
case 'config':
return new UiSettingsClient(options);
case 'config-global':
return new UiSettingsGlobalClient(options);
case 'config-user':
return new UiSettingsUserClient(options, request);
default:
throw new Error('Unsupported client error');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 type { KibanaRequest } from '@kbn/core-http-server';
import type { IUserUiSettingsClient } from '@kbn/core-ui-settings-server/src';
import type { UiSettingsServiceOptions } from '../types';
import { BaseUiSettingsClient } from './base_ui_settings_client';

interface UserProvidedValue<T = unknown> {
userValue?: T;
isOverridden?: boolean;
}

type UserProvided<T = unknown> = Record<string, UserProvidedValue<T>>;

/**
* Common logic for setting / removing keys in a {@link IUserUiSettingsClient} implementation
*/
export class UiSettingsUserClient extends BaseUiSettingsClient implements IUserUiSettingsClient {
private readonly userProfileSettingsClient: UiSettingsServiceOptions['userProfileSettingsClient'];
private readonly request?: KibanaRequest;

constructor(options: UiSettingsServiceOptions, request?: KibanaRequest) {
Copy link
Contributor

@majagrubic majagrubic Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not take a request. Have at my comment above, the flow should be:
renderingService -> userProfileService/themingService/howeverYouWishToCallIt -> userProfileSettingsClient

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call! I am now passing in the the request from the renderingService to the uiSettingsUserClient

super(options);
const { userProfileSettingsClient } = options;
this.userProfileSettingsClient = userProfileSettingsClient;
this.request = request;
}

async getUserProfileSettings(): Promise<Record<string, string>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: any reason for introducing the new API getUserProfileSettings instead of returning these values in the API getUserProvided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am now passing in the request to the getUserProfileSettings function based on other code review feedback, by adding the request as an argument, I would break the interface.

let result = {} as Record<string, string>;

if (this.request) {
result = (await this.userProfileSettingsClient?.get(this.request)) || {};
} else {
this.log.warn('Request not set, unable to retrieve User Settings');
}

return result;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following methods are left stubbed to complete the interface. While not used currently, the savedObjectsClient is available here for a future iteration of User Settings that may be stored/migrated to SO

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention to create one SO per user to hold other User Settings? What's the benefit we'd get from doing so instead of storing every User Setting in the user's profile? Any known limitations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any limitations for for simple settings, like Dark Mode, being stored in the User Profile, but Im sure there could arise a scenario that wouldn't work.

In my initial meeting with the core team and discussions on my brainstorming doc, the team expressed that there might be a need to store User Settings in SO eventually, or at least, be able to migrate the settings from User Profiles to SO in the future.

I wanted the client to behave like the regular/global client so I included the methods stubbed so they would fulfill the interface and be available if we do need to move that direction

async getUserProvided<T = unknown>(): Promise<UserProvided<T>> {
this.log.warn('`getUserProvided` operation is not supported for User Settings.');
return {};
}

async setMany(changes: Record<string, any>) {
this.log.warn('`setMany` operation is not supported for User Settings.');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of trying to replicate the same interface as the other UISettings clients then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the early design concerns, I wanted to so stub a path so that if we did need to build out user settings stored/migrated to SO, it would be ready to be filled out.

Maybe this makes more sense with my suggestion to align all the client interfaces here?


async set(key: string, value: any) {
this.log.warn('`set` operation is not supported for User Settings.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is set not provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the other clients, this method deals with writing SO.

The uiSettingsUserClient only reads from the userSettingsService in the Security Plugin. The value is set through the userProfileService.update(...)

I've left the methods stubbed so that uiSettingsUserClient matches the interface that uiSettingsClient/uiSettingsGlobalClient implement.

}

async remove(key: string) {
this.log.warn('`remove` operation is not supported for User Settings.');
}

async removeMany(keys: string[]) {
this.log.warn('`removeMany` operation is not supported for User Settings.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface Options {
buildNum: number;
log: Logger;
handleWriteErrors: boolean;
type: 'config' | 'config-global';
type: 'config' | 'config-global' | 'config-user';
}

export async function createOrUpgradeSavedConfig(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export async function getUpgradeableConfig({
}: {
savedObjectsClient: SavedObjectsClientContract;
version: string;
type: 'config' | 'config-global';
type: 'config' | 'config-global' | 'config-user';
}) {
// attempt to find a config we can upgrade
const { saved_objects: savedConfigs } =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type { UiSettingsParams } from '@kbn/core-ui-settings-common';
import type { Logger } from '@kbn/logging';
import { UserProfileSettingsClientContract } from '@kbn/core-ui-settings-server/src/contracts';

/** @internal */
export interface InternalUiSettingsServicePreboot {
Expand All @@ -31,11 +32,12 @@ export type InternalUiSettingsServiceStart = UiSettingsServiceStart;

/** @internal */
export interface UiSettingsServiceOptions {
type: 'config' | 'config-global';
type: 'config' | 'config-global' | 'config-user';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? My understanding was that the user settings are not actually persisted to a SO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the UiSettingsUserClient does not currently provide functionality to save settings in SO (those methods are stubbed out), it acts like the UiSettingsClient and UiSettingsGlobalClient so if the future it could be fixed for SO settings/migrations

id: string;
buildNum: number;
savedObjectsClient: SavedObjectsClientContract;
overrides?: Record<string, any>;
defaults?: Record<string, UiSettingsParams>;
log: Logger;
userProfileSettingsClient?: UserProfileSettingsClientContract;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import type { CoreSavedObjectsRouteHandlerContext } from '@kbn/core-saved-object
import type {
UiSettingsRequestHandlerContext,
IUiSettingsClient,
IUserUiSettingsClient,
} from '@kbn/core-ui-settings-server';
import { KibanaRequest } from '@kbn/core-http-server';
import type { InternalUiSettingsServiceStart } from './types';

/**
Expand All @@ -20,10 +22,12 @@ import type { InternalUiSettingsServiceStart } from './types';
export class CoreUiSettingsRouteHandlerContext implements UiSettingsRequestHandlerContext {
#client?: IUiSettingsClient;
#globalClient?: IUiSettingsClient;
#userClient?: IUserUiSettingsClient;

constructor(
private readonly uiSettingsStart: InternalUiSettingsServiceStart,
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext
private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext,
private readonly request: KibanaRequest
) {}

public get client() {
Expand All @@ -43,4 +47,14 @@ export class CoreUiSettingsRouteHandlerContext implements UiSettingsRequestHandl
}
return this.#globalClient;
}

public get userClient() {
if (this.#userClient == null) {
this.#userClient = this.uiSettingsStart.userAsScopedToClient(
this.savedObjectsRouterHandlerContext.client,
this.request
);
}
return this.#userClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
import type { InternalSavedObjectsServiceSetup } from '@kbn/core-saved-objects-server-internal';
import type { UiSettingsParams, UiSettingsScope } from '@kbn/core-ui-settings-common';
import type { KibanaRequest } from '@kbn/core-http-server';
import type {
UserProfileSettingsClientContract,
UserProfileSettingsClientFactoryProvider,
} from '@kbn/core-ui-settings-server/src/contracts';
import { UiSettingsConfigType, uiSettingsConfig as uiConfigDefinition } from './ui_settings_config';
import { UiSettingsClient, UiSettingsClientFactory, UiSettingsGlobalClient } from './clients';
import {
UiSettingsClient,
UiSettingsClientFactory,
UiSettingsGlobalClient,
UiSettingsUserClient,
} from './clients';
import type {
InternalUiSettingsServicePreboot,
InternalUiSettingsServiceSetup,
Expand All @@ -36,6 +46,8 @@ type ClientType<T> = T extends 'global'
? UiSettingsGlobalClient
: T extends 'namespace'
? UiSettingsClient
: T extends 'user'
? UiSettingsUserClient
: never;

/** @internal */
Expand All @@ -48,6 +60,7 @@ export class UiSettingsService
private readonly uiSettingsDefaults = new Map<string, UiSettingsParams>();
private readonly uiSettingsGlobalDefaults = new Map<string, UiSettingsParams>();
private overrides: Record<string, any> = {};
private userProfileSettingsClientFactoryProvider?: UserProfileSettingsClientFactoryProvider;

constructor(private readonly coreContext: CoreContext) {
this.log = coreContext.logger.get('ui-settings-service');
Expand Down Expand Up @@ -96,11 +109,22 @@ export class UiSettingsService
return {
asScopedToClient: this.getScopedClientFactory('namespace'),
globalAsScopedToClient: this.getScopedClientFactory('global'),
userAsScopedToClient: this.getUserClientFactory<'user'>(),
setUserProfileSettingsClientFactoryProvider:
this.setUserProfileSettingsClientFactoryProvider(),
};
}

public async stop() {}

private setUserProfileSettingsClientFactoryProvider(): (
provider: UserProfileSettingsClientFactoryProvider
) => void {
return (provider: UserProfileSettingsClientFactoryProvider) => {
this.userProfileSettingsClientFactoryProvider = provider;
};
}

private getScopedClientFactory<T extends UiSettingsScope>(
scope: UiSettingsScope
): (savedObjectsClient: SavedObjectsClientContract) => ClientType<T> {
Expand All @@ -118,10 +142,43 @@ export class UiSettingsService
overrides: isNamespaceScope ? this.overrides : {},
log: this.log,
};

return UiSettingsClientFactory.create(options) as ClientType<T>;
};
}

private getUserClientFactory<T extends UiSettingsScope>(): (
savedObjectsClient: SavedObjectsClientContract,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request: KibanaRequest
) => ClientType<T> {
const { version, buildNum } = this.coreContext.env.packageInfo;

return (
savedObjectsClient: SavedObjectsClientContract,
request: KibanaRequest
): ClientType<T> => {
let userProfileSettingsClient: UserProfileSettingsClientContract | undefined;

if (this.userProfileSettingsClientFactoryProvider) {
const userProfilesClientFactory = this.userProfileSettingsClientFactoryProvider();
userProfileSettingsClient = userProfilesClientFactory();
}

const options = {
id: version,
buildNum,
log: this.log,
savedObjectsClient,
type: 'config-user' as 'config' | 'config-global' | 'config-user',
defaults: {},
...(userProfileSettingsClient && { userProfileSettingsClient }),
overrides: {},
};

return UiSettingsClientFactory.create(options, request) as ClientType<T>;
};
}

private register = (settings: Record<string, UiSettingsParams> = {}) => {
Object.entries(settings).forEach(([key, value]) => {
if (this.uiSettingsDefaults.has(key)) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/ui-settings/core-ui-settings-server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

export type {
IUiSettingsClient,
IUserUiSettingsClient,
UiSettingsServiceSetup,
UiSettingsServiceStart,
UiSettingsRequestHandlerContext,
Expand Down
Loading