[CPS] Introduce kbn-project-routing header for simplified project_routing propagation#256839
Closed
gsoldevila wants to merge 8 commits intoelastic:mainfrom
Closed
[CPS] Introduce kbn-project-routing header for simplified project_routing propagation#256839gsoldevila wants to merge 8 commits intoelastic:mainfrom
gsoldevila wants to merge 8 commits intoelastic:mainfrom
Conversation
This was referenced Mar 10, 2026
…ting propagation Adds a new `projectRouting: 'request-header'` option to `IClusterClient.asScoped()`. When selected, the scoped Elasticsearch client reads the `project_routing` value from the `kbn-project-routing` HTTP header on the incoming request instead of requiring callers to thread the value through request bodies and service layers. - Adds `KBN_PROJECT_ROUTING_HEADER` constant to `@kbn/cps-common` and `@kbn/cps-server-utils` - Adds `RequestHeaderRouting` interface to the public `@kbn/core-elasticsearch-server` API - Refactors `OnRequestHandlerFactory` (internal) from a mixed union with an embedded `ScopeableUrlRequest` to a proper discriminated union (`FactoryRoutingOpts`), making the `'space'` and `'request-header'` cases explicitly typed - Falls back to `'origin-only'` routing when the header is absent - Updates the `ClusterClientMock` to accept `(request, opts?)` for all overloads Closes elastic/kibana-team#3032 Made-with: Cursor
…uting The Kibana HTTP client rejects headers starting with 'kbn-', so the header must use the 'x-kbn-' prefix instead. Made-with: Cursor
a01d506 to
edbe973
Compare
…load - Renames `projectRouting: 'space'` to `'space-npre'` in FactoryRoutingOpts, AsScopedOptions, SpaceNPRERouting, and all call sites/tests, to make the Named Project Routing Expression intent explicit in the option name. - Absorbs `logger` into FactoryRoutingOpts (via CommonFactoryRoutingOpts intersection) so the factory signature is self-contained. - Adds a broad `asScoped(request: KibanaRequest, opts?: AsScopedOptions)` overload to IClusterClient, allowing callers that hold an unnarrowed AsScopedOptions to call asScoped without manual routing dispatch. - Removes the now-redundant `createScopedEsClient` helper in SearchService, replacing both call sites with a direct `elasticsearch.client.asScoped(request, opts)`. Made-with: Cursor
…ining callers The asScoped(request: KibanaRequest, opts?: AsScopedOptions) overload on IClusterClient changed the ClusterClientMock signature to require a request argument, breaking all tests that call asScoped() with no args (actions_telemetry, security user_profile, etc.). Changes: - Remove the broad overload and the KibanaRequest import from IClusterClient - Revert ClusterClientMock.asScoped back to () => ScopedClusterClientMock - Restore createScopedEsClient in SearchService with a switch on projectRouting (handles 'space-npre', 'all', 'origin-only', 'request-header', and the default no-opts case) - Fix the remaining 'space' -> 'space-npre' renames that CI caught: search_service.test.ts (description text), alerting/get_executor_services.ts (constant + comment), alerting/get_executor_services.test.ts (SpaceNPRERouting literal) Made-with: Cursor
Remove 'origin-only' and 'all' from the public AsScopedOptions. These
were never intended as general-purpose options for plugin developers:
- 'origin-only' is the implicit default when asScoped() is called with no
opts, and is used internally by asInternalUser (FactoryRoutingOpts keeps
it as an internal detail).
- 'all' has no public callsites; broadcasting to all projects can be
achieved client-side by setting the x-kbn-project-routing header to
'_alias:*' and relying on 'request-header' routing.
The two remaining public options are:
- 'space-npre': route to the NPRE configured for the current Kibana space
(requires a ScopeableUrlRequest so the space can be extracted from the URL)
- 'request-header': read project_routing from the x-kbn-project-routing
request header; falls back to origin-only when the header is absent
This makes the ClusterClient.asScoped() switch trivial enough to inline,
so the createScopedEsClient helper in SearchService is removed in favour
of a direct asScoped(request, opts ?? { projectRouting: 'request-header' })
call. The 'request-header' default ensures that search requests issued by
the data plugin always respect the project selection made by the user in
the CPS project picker.
Changes:
- Remove OriginOnlyRouting and AllProjectsRouting interfaces from the
public package (kept internally in FactoryRoutingOpts)
- Narrow AsScopedOptions.projectRouting to 'space-npre' | 'request-header'
- Update ClusterClient.asScoped overloads and implementation body
(no-opts → 'origin-only' internally; opts present → 'space-npre' or
'request-header')
- Remove createScopedEsClient from SearchService; inline the asScoped call
- Update cluster_client.test.ts and search_service.test.ts accordingly
Made-with: Cursor
Contributor
⏳ Build in-progress, with failures
Failed CI Steps
Test Failures
History
|
Member
Author
|
Will be tackled on #256844 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes elastic/kibana-team#3032
Introduces a
kbn-project-routingHTTP header as a cleaner propagation channel for CPSproject_routing, eliminating the need to thread the value through request bodies and service layers on every app that integrates with CPS.Currently apps must pass
projectRoutingin the HTTP body, extract it server-side, and inject it explicitly intoasScoped(). This pattern was established in #250666 and #253654 and would need to be replicated by every app that opts into CPS.With this change, apps only need to:
{ 'kbn-project-routing': value }in the HTTP request headers via the Kibana HTTP clientesClient.asScoped(request, { projectRouting: 'request-header' })Changes
New
kbn-project-routingheader constant added to@kbn/cps-commonand@kbn/cps-server-utils.New
RequestHeaderRoutinginterface andasScopedoverload in the public@kbn/core-elasticsearch-serverAPI. Falls back toorigin-onlyrouting when the header is absent.OnRequestHandlerFactoryrefactored (internal type) from a mixed union whereScopeableUrlRequestwas embedded directly in theprojectRoutingfield, to a proper discriminated union (FactoryRoutingOpts). The'space'and'request-header'cases now carry their request objects explicitly as a typedrequestfield.ClusterClientMock.asScopedupdated to accept(request: ScopeableRequest, opts?: AsScopedOptions)to cover all overloads correctly.Testing
cps_request_handler_factory.test.ts: header present, header absent (fallback), header as array (first value taken), CPS disabled (strips regardless)cluster_client.test.ts: header routing and absent-header fallbackFollow-up
A separate PR (branched from this one) will demonstrate adoption by simplifying the
dataplugin's existingprojectRoutingbody-propagation path to use the header instead.Made with Cursor