Skip to content

Commit 9f1b56b

Browse files
tsullivankibanamachineDosant
authored andcommitted
[User Storage Service] browser client, React hooks, injection rendering (elastic#268312)
Epic: elastic/kibana-team#2769 ## Summary Adds the browser foundation for the Core User Storage service (Step 2 of the project; the server-side foundation landed in elastic#256912). Three pieces: **A. Server-rendered injection.** - RenderingService` pre-fetches the current user's stored values per request via `userStorage.asScoped(request).getAll()` and inlines them under a new `userStorage.values` field on `<kbn-injected-metadata>`. - Bounded by a 50ms timeout (mirroring the existing `clusterInfo` pattern at `rendering_service.tsx:234`) - skipped for anonymous pages and when `asScoped` returns `null` (no `profile_uid`). **B. Browser client.** - Three new packages in `src/core/packages/user-storage/`: 1. `@kbn/core-user-storage-browser` (public API: `IUserStorageClient`, `UserStorageProvider`, `useUserStorage`, `useUserStorageClient`) 2. `@kbn/core-user-storage-browser-internal` (`UserStorageService`, `UserStorageClient`, `UserStorageApi`) 3. `@kbn/core-user-storage-browser-mocks`. - The client seeds an in-memory cache from injected metadata at boot, so `get(key)` is synchronous at first paint. `set`/`remove` write through HTTP and refresh the cache on success. - Architectural diagram <img width="1295" height="631" alt="image" src="https://github.com/user-attachments/assets/0b3e9113-9ef1-47d7-8f7d-37bf8730e432" /> **C. React hook + provider.** - `useUserStorage(key, default)` returns `[value, setter]` with an Observable-backed subscription, and throws a helpful error when used outside `<UserStorageProvider>`. - `useUserStorageClient()` is the escape hatch for consumers that depend on `remove`, `getUpdate$`, and `getUpdateErrors$`. ## Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - ~[ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)~ - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - ~[ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~ - ~[ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations.~ - ~[ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed~ - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Anton Dosov <anton.dosov@elastic.co>
1 parent 84ae422 commit 9f1b56b

91 files changed

Lines changed: 2419 additions & 109 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,11 +349,13 @@ src/core/packages/user-profile/server-mocks @elastic/kibana-core
349349
src/core/packages/user-settings/server @elastic/kibana-security
350350
src/core/packages/user-settings/server-internal @elastic/kibana-security
351351
src/core/packages/user-settings/server-mocks @elastic/kibana-security
352+
src/core/packages/user-storage/browser @elastic/appex-sharedux
353+
src/core/packages/user-storage/browser-internal @elastic/appex-sharedux
354+
src/core/packages/user-storage/browser-mocks @elastic/appex-sharedux
352355
src/core/packages/user-storage/common @elastic/appex-sharedux
353356
src/core/packages/user-storage/server @elastic/appex-sharedux
354357
src/core/packages/user-storage/server-internal @elastic/appex-sharedux
355358
src/core/packages/user-storage/server-mocks @elastic/appex-sharedux
356-
src/core/packages/user-storage/test/plugins/user_storage_test @elastic/appex-sharedux
357359
src/core/test-helpers/kbn-server @elastic/kibana-core
358360
src/core/test-helpers/model-versions @elastic/kibana-core
359361
src/platform/kbn-ui/chrome-layout @elastic/appex-sharedux
@@ -927,6 +929,7 @@ src/platform/test/plugin_functional/plugins/ui_settings_plugin @elastic/kibana-c
927929
src/platform/test/plugin_functional/plugins/usage_collection @elastic/kibana-core
928930
src/platform/test/server_integration/plugins/status_plugin_a @elastic/kibana-core
929931
src/platform/test/server_integration/plugins/status_plugin_b @elastic/kibana-core
932+
src/platform/test/user_storage/plugins/user_storage_test @elastic/appex-sharedux
930933
src/setup_node_env @elastic/kibana-operations
931934
x-pack/examples/alerting_example @elastic/response-ops
932935
x-pack/examples/embedded_lens_example @elastic/kibana-visualizations

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@
533533
"@kbn/core-user-settings-server": "link:src/core/packages/user-settings/server",
534534
"@kbn/core-user-settings-server-internal": "link:src/core/packages/user-settings/server-internal",
535535
"@kbn/core-user-settings-server-mocks": "link:src/core/packages/user-settings/server-mocks",
536+
"@kbn/core-user-storage-browser": "link:src/core/packages/user-storage/browser",
537+
"@kbn/core-user-storage-browser-internal": "link:src/core/packages/user-storage/browser-internal",
536538
"@kbn/core-user-storage-common": "link:src/core/packages/user-storage/common",
537539
"@kbn/core-user-storage-server": "link:src/core/packages/user-storage/server",
538540
"@kbn/core-user-storage-server-internal": "link:src/core/packages/user-storage/server-internal",
@@ -1245,7 +1247,7 @@
12451247
"@kbn/user-profile-components": "link:src/platform/packages/shared/kbn-user-profile-components",
12461248
"@kbn/user-profile-examples-plugin": "link:examples/user_profile_examples",
12471249
"@kbn/user-profiles-consumer-plugin": "link:x-pack/platform/test/security_api_integration/plugins/user_profiles_consumer",
1248-
"@kbn/user-storage-test-plugin": "link:src/core/packages/user-storage/test/plugins/user_storage_test",
1250+
"@kbn/user-storage-test-plugin": "link:src/platform/test/user_storage/plugins/user_storage_test",
12491251
"@kbn/utility-types": "link:src/platform/packages/shared/kbn-utility-types",
12501252
"@kbn/utility-types-jest": "link:src/platform/packages/shared/kbn-utility-types-jest",
12511253
"@kbn/utils": "link:src/platform/packages/shared/kbn-utils",
@@ -1710,6 +1712,7 @@
17101712
"@kbn/core-ui-settings-server-mocks": "link:src/core/packages/ui-settings/server-mocks",
17111713
"@kbn/core-usage-data-server-mocks": "link:src/core/packages/usage-data/server-mocks",
17121714
"@kbn/core-user-activity-server-mocks": "link:src/core/packages/user-activity/server-mocks",
1715+
"@kbn/core-user-storage-browser-mocks": "link:src/core/packages/user-storage/browser-mocks",
17131716
"@kbn/cypress-config": "link:src/platform/packages/shared/kbn-cypress-config",
17141717
"@kbn/cypress-test-helper": "link:src/platform/packages/shared/kbn-cypress-test-helper",
17151718
"@kbn/dependency-ownership": "link:packages/kbn-dependency-ownership",

packages/kbn-optimizer/limits.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pageLoadAssetSize:
2929
contentConnectors: 33014
3030
contentManagement: 8350
3131
controls: 10300
32-
core: 547151
32+
core: 548600
3333
cps: 9209
3434
crossClusterReplication: 12662
3535
customIntegrations: 11715

src/core/packages/injected-metadata/browser-internal/src/injected_metadata_service.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,17 @@ describe('setup.getFeatureFlags()', () => {
195195
expect(contract.getFeatureFlags()).toBeUndefined();
196196
});
197197
});
198+
199+
describe('setup.getUserStorage()', () => {
200+
it('returns injectedMetadata.userStorage', () => {
201+
const injectedMetadata = new InjectedMetadataService({
202+
injectedMetadata: {
203+
userStorage: { values: { 'navigation:layout': { hidden: ['discover'] } } },
204+
},
205+
} as unknown as InjectedMetadataParams);
206+
207+
expect(injectedMetadata.setup().getUserStorage()).toEqual({
208+
values: { 'navigation:layout': { hidden: ['discover'] } },
209+
});
210+
});
211+
});

src/core/packages/injected-metadata/browser-internal/src/injected_metadata_service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export class InjectedMetadataService {
9999
getFeatureFlags: () => {
100100
return this.state.featureFlags;
101101
},
102+
103+
getUserStorage: () => {
104+
return this.state.userStorage;
105+
},
102106
};
103107
}
104108
}

src/core/packages/injected-metadata/browser-internal/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ export interface InternalInjectedMetadataSetup {
6464
initialFeatureFlags: Record<string, unknown>;
6565
}
6666
| undefined;
67+
getUserStorage: () => {
68+
values: Record<string, unknown>;
69+
};
6770
}
6871

6972
/** @internal */

src/core/packages/injected-metadata/browser-mocks/src/injected_metadata_service.mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const createSetupContractMock = () => {
5454
getKibanaBuildNumber: jest.fn(),
5555
getCustomBranding: jest.fn(),
5656
getFeatureFlags: jest.fn(),
57+
getUserStorage: jest.fn().mockReturnValue({ values: {} }),
5758
});
5859

5960
return setupContract;

src/core/packages/injected-metadata/common-internal/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,7 @@ export interface InjectedMetadata {
9393
};
9494
};
9595
customBranding: Pick<CustomBranding, 'logo' | 'customizedLogo' | 'pageTitle'>;
96+
userStorage: {
97+
values: Record<string, unknown>;
98+
};
9699
}

src/core/packages/lifecycle/browser-mocks/moon.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ dependsOn:
3737
- '@kbn/core-rendering-browser-mocks'
3838
- '@kbn/core-pricing-browser-mocks'
3939
- '@kbn/core-di-mocks'
40+
- '@kbn/core-user-storage-browser-mocks'
4041
- '@kbn/lazy-object'
4142
tags:
4243
- shared-browser

src/core/packages/lifecycle/browser-mocks/src/core_setup.mock.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
2424
import { createCoreStartMock } from './core_start.mock';
2525
import { coreFeatureFlagsMock } from '@kbn/core-feature-flags-browser-mocks';
2626
import { chromeServiceMock } from '@kbn/core-chrome-browser-mocks';
27+
import { userStorageServiceMock } from '@kbn/core-user-storage-browser-mocks';
2728
import { lazyObject } from '@kbn/lazy-object';
2829

2930
export function createCoreSetupMock({
@@ -56,6 +57,7 @@ export function createCoreSetupMock({
5657
theme: themeServiceMock.createSetupContract(),
5758
security: securityServiceMock.createSetup(),
5859
userProfile: userProfileServiceMock.createSetup(),
60+
userStorage: userStorageServiceMock.createSetupContract(),
5961
plugins: lazyObject({
6062
onSetup: jest.fn(),
6163
onStart: jest.fn(),

0 commit comments

Comments
 (0)