Skip to content

Commit 6f60a5c

Browse files
committed
attend to review feedback
1 parent bad02a6 commit 6f60a5c

4 files changed

Lines changed: 103 additions & 15 deletions

File tree

src/core/packages/user-profile/common/src/user_profile.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,29 @@ export interface UserProfileUserInfo {
6060
}
6161

6262
/**
63-
* Placeholder for data stored in user profile.
63+
* Placeholder for data stored in user profile,
64+
* services that store data in the user profile should specify said data by augmenting this type in it's implementation,
65+
* like so:
66+
*
67+
* @example
68+
* ```ts
69+
* declare module '@kbn/core-user-profile-common' {
70+
* interface UserProfileData {
71+
* myService: {
72+
* myData: string;
73+
* };
74+
* }
75+
* }
76+
* ```
77+
* This will make it such that the return value for the invocation of `getCurrent` is typed matching the defined augmentation
78+
*
79+
* ```ts
80+
* const userProfile = await userProfileService.getCurrent();
81+
* // accessing 'myService.myData' is now typed as 'string'
82+
* console.log(userProfile.data.myService.myData);
83+
* ```
6484
*/
65-
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- This is intentional, so that consumers can augment this type with values they intend to store in the user profile data
85+
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- See the comment above for an explanation.
6686
export interface UserProfileData {}
6787

6888
/**

src/core/packages/user-settings/server-internal/src/user_settings_service.test.ts

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import { mockCoreContext } from '@kbn/core-base-server-mocks';
1111
import { httpServerMock } from '@kbn/core-http-server-mocks';
1212
import { userProfileServiceMock } from '@kbn/core-user-profile-server-mocks';
13-
import type { UserProfileWithSecurity } from '@kbn/core-user-profile-common';
13+
import type { UserProfileWithSecurity, UserProfileData } from '@kbn/core-user-profile-common';
1414
import { UserSettingsService } from './user_settings_service';
1515

1616
describe('#setup', () => {
@@ -26,18 +26,18 @@ describe('#setup', () => {
2626
};
2727
});
2828

29-
const createUserProfile = (darkMode: string | undefined): UserProfileWithSecurity => {
29+
const createUserProfile = (
30+
userSettings: Partial<NonNullable<UserProfileData['userSettings']>>
31+
): UserProfileWithSecurity => {
3032
return {
3133
data: {
32-
userSettings: {
33-
darkMode,
34-
},
34+
userSettings,
3535
},
3636
} as unknown as UserProfileWithSecurity;
3737
};
3838

3939
it('fetches userSettings when client is set and returns `true` when `darkMode` is set to `dark`', async () => {
40-
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile('dark'));
40+
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile({ darkMode: 'dark' }));
4141

4242
const { getUserSettingDarkMode } = service.setup();
4343
service.start(startDeps);
@@ -54,7 +54,7 @@ describe('#setup', () => {
5454
});
5555

5656
it('fetches userSettings when client is set and returns `false` when `darkMode` is set to `light`', async () => {
57-
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile('light'));
57+
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile({ darkMode: 'light' }));
5858

5959
const { getUserSettingDarkMode } = service.setup();
6060
service.start(startDeps);
@@ -71,7 +71,7 @@ describe('#setup', () => {
7171
});
7272

7373
it('fetches userSettings when client is set and returns `system` when `darkMode` is set to `system`', async () => {
74-
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile('system'));
74+
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile({ darkMode: 'system' }));
7575

7676
const { getUserSettingDarkMode } = service.setup();
7777
service.start(startDeps);
@@ -88,7 +88,7 @@ describe('#setup', () => {
8888
});
8989

9090
it('fetches userSettings when client is set and returns `undefined` when `darkMode` is set to `` (the default value)', async () => {
91-
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile(''));
91+
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile({ darkMode: undefined }));
9292

9393
const { getUserSettingDarkMode } = service.setup();
9494
service.start(startDeps);
@@ -105,7 +105,9 @@ describe('#setup', () => {
105105
});
106106

107107
it('fetches userSettings when client is set and returns `undefined` when `darkMode` is set to `space_default`', async () => {
108-
startDeps.userProfile.getCurrent.mockResolvedValue(createUserProfile('space_default'));
108+
startDeps.userProfile.getCurrent.mockResolvedValue(
109+
createUserProfile({ darkMode: 'space_default' })
110+
);
109111

110112
const { getUserSettingDarkMode } = service.setup();
111113
service.start(startDeps);
@@ -121,6 +123,63 @@ describe('#setup', () => {
121123
});
122124
});
123125

126+
it('fetches userSettings when client is set and returns `true` when `rememberSelectedSpace` is set to `true`', async () => {
127+
startDeps.userProfile.getCurrent.mockResolvedValue(
128+
createUserProfile({ rememberSelectedSpace: true })
129+
);
130+
131+
const { getUserSettingRememberSelectedSpace } = service.setup();
132+
service.start(startDeps);
133+
134+
const kibanaRequest = httpServerMock.createKibanaRequest();
135+
const rememberSelectedSpace = await getUserSettingRememberSelectedSpace(kibanaRequest);
136+
137+
expect(rememberSelectedSpace).toEqual(true);
138+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledTimes(1);
139+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledWith({
140+
request: kibanaRequest,
141+
dataPath: 'userSettings',
142+
});
143+
});
144+
145+
it('fetches userSettings when client is set and returns `false` when `rememberSelectedSpace` is set to `false`', async () => {
146+
startDeps.userProfile.getCurrent.mockResolvedValue(
147+
createUserProfile({ rememberSelectedSpace: false })
148+
);
149+
150+
const { getUserSettingRememberSelectedSpace } = service.setup();
151+
service.start(startDeps);
152+
153+
const kibanaRequest = httpServerMock.createKibanaRequest();
154+
const rememberSelectedSpace = await getUserSettingRememberSelectedSpace(kibanaRequest);
155+
156+
expect(rememberSelectedSpace).toEqual(false);
157+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledTimes(1);
158+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledWith({
159+
request: kibanaRequest,
160+
dataPath: 'userSettings',
161+
});
162+
});
163+
164+
it('fetches userSettings when client is set and returns `true` when `rememberSelectedSpace` is not set', async () => {
165+
startDeps.userProfile.getCurrent.mockResolvedValue(
166+
createUserProfile({ rememberSelectedSpace: undefined })
167+
);
168+
169+
const { getUserSettingRememberSelectedSpace } = service.setup();
170+
service.start(startDeps);
171+
172+
const kibanaRequest = httpServerMock.createKibanaRequest();
173+
const rememberSelectedSpace = await getUserSettingRememberSelectedSpace(kibanaRequest);
174+
175+
expect(rememberSelectedSpace).toEqual(true);
176+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledTimes(1);
177+
expect(startDeps.userProfile.getCurrent).toHaveBeenCalledWith({
178+
request: kibanaRequest,
179+
dataPath: 'userSettings',
180+
});
181+
});
182+
124183
it('does not fetch userSettings when client is not set, returns `undefined`, and logs a debug statement', async () => {
125184
const { getUserSettingDarkMode } = service.setup();
126185

x-pack/platform/plugins/shared/security/server/routes/user_profile/update.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ describe('Update profile routes', () => {
306306
userSettings: {
307307
darkMode: 'dark',
308308
contrastMode: 'high',
309+
rememberSelectedSpace: true,
309310
},
310311
};
311312

x-pack/platform/plugins/shared/security/server/routes/user_profile/update.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
*/
77

88
import { schema } from '@kbn/config-schema';
9+
import type { UserProfileData } from '@kbn/core-user-profile-common';
910
import { isValidUserProfileAvatarColor } from '@kbn/user-profile-components';
11+
import type { DotKeysOf } from '@kbn/utility-types';
1012

1113
import type { RouteDefinitionParams } from '..';
1214
import { IMAGE_FILE_TYPES } from '../../../common/constants';
@@ -15,6 +17,9 @@ import { flattenObject } from '../../lib';
1517
import { getPrintableSessionId } from '../../session_management';
1618
import { createLicensedRouteHandler } from '../licensed_route_handler';
1719

20+
type UserProfileUpdatePayload = Pick<UserProfileData, 'userSettings'>;
21+
type UserProfileUpdatePaths = DotKeysOf<UserProfileUpdatePayload>;
22+
1823
/** User profile data keys that are allowed to be updated by Cloud users */
1924
const ALLOWED_KEYS_UPDATE_CLOUD = [
2025
'userSettings.darkMode',
@@ -23,7 +28,7 @@ const ALLOWED_KEYS_UPDATE_CLOUD = [
2328
'userSettings.agentBuilderAnnouncementModalSeenBySpaceJson',
2429
'userSettings.locale',
2530
'userSettings.rememberSelectedSpace',
26-
];
31+
] as const satisfies readonly UserProfileUpdatePaths[];
2732

2833
const MAX_STRING_FIELD_LENGTH = 1024;
2934

@@ -59,7 +64,9 @@ const userProfileUpdateSchema = schema.object({
5964
),
6065
locale: schema.maybe(schema.string({ maxLength: MAX_STRING_FIELD_LENGTH })),
6166
rememberSelectedSpace: schema.maybe(schema.boolean()),
62-
lastSelectedSpaceId: schema.nullable(schema.string({ maxLength: MAX_STRING_FIELD_LENGTH })),
67+
lastSelectedSpaceId: schema.maybe(
68+
schema.nullable(schema.string({ maxLength: MAX_STRING_FIELD_LENGTH }))
69+
),
6370
})
6471
),
6572
});
@@ -157,8 +164,9 @@ export function defineUpdateUserProfileDataRoute({
157164
if (currentUser?.elastic_cloud_user) {
158165
// We only allow specific user profile data to be updated by Elastic Cloud SSO users.
159166
const isUpdateAllowed = keysToUpdate.every((key) =>
160-
ALLOWED_KEYS_UPDATE_CLOUD.includes(key)
167+
(ALLOWED_KEYS_UPDATE_CLOUD as readonly string[]).includes(key)
161168
);
169+
162170
if (keysToUpdate.length === 0 || !isUpdateAllowed) {
163171
logger.warn(
164172
`Elastic Cloud SSO users aren't allowed to update profiles in Kibana. (sid: ${getPrintableSessionId(

0 commit comments

Comments
 (0)