Skip to content

[CPS] Add @kbn/cps-server-utils package#254085

Merged
gsoldevila merged 2 commits into
elastic:mainfrom
gsoldevila:kbn-cps-server-utils
Feb 20, 2026
Merged

[CPS] Add @kbn/cps-server-utils package#254085
gsoldevila merged 2 commits into
elastic:mainfrom
gsoldevila:kbn-cps-server-utils

Conversation

@gsoldevila
Copy link
Copy Markdown
Member

@gsoldevila gsoldevila commented Feb 19, 2026

Summary

Introduces @kbn/cps-server-utils, a new shared-server package that exposes a getSpaceNPRE utility for computing the Named Project Routing Expression (NPRE) for a Kibana space.

The NPRE follows the convention kibana_space_${spaceId}_default, which identifies the default NPRE for a given space in Elasticsearch.

The function accepts two forms:

  • getSpaceNPRE(spaceIdOrRequest: string | KibanaRequest)
    • string: directly from a space ID (empty string falls back to "default")
    • KibanaRequest: extracts the space from the request URL path without depending on the spaces plugin

Made with Cursor

Introduces a new server-side package that exposes `getSpaceNPRE`, a utility
to compute the Named Project Routing Expression (NPRE) for a given Kibana
space. Accepts either a space ID string or a `KibanaRequest` (extracting the
space from the URL path without depending on the `spaces` plugin).

The NPRE follows the convention `kibana_space_${spaceId}_default`, mapping
to the default data view for a given space.

Co-authored-by: Cursor <cursoragent@cursor.com>
@gsoldevila gsoldevila added Team:Core Platform Core services: plugins, logging, config, saved objects, http, ES client, i18n, etc t// release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting labels Feb 19, 2026
@gsoldevila gsoldevila marked this pull request as ready for review February 19, 2026 20:41
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/kibana-core (Team:Core)

@elasticmachine
Copy link
Copy Markdown
Contributor

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/cps-server-utils - 2 +2
Unknown metric groups

API count

id before after diff
@kbn/cps-server-utils - 2 +2

History

export function deleteAction(drilldownState: DrilldownActionState) {
const { actionId, trigger } = drilldownState;
if (!uiActions.hasAction(actionId)) return;
export function getSpaceNPRE(spaceIdOrRequest: string | KibanaRequest): string {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: can you add TSDocs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Will add them in a follow-up PR, I don't want to retrigger a whole CI just for that.

@gsoldevila gsoldevila merged commit e8be676 into elastic:main Feb 20, 2026
14 checks passed
gsoldevila added a commit that referenced this pull request Feb 23, 2026
Part of elastic/kibana-team#2829

## Summary

Builds on #254085 (`@kbn/cps-server-utils`) to allow callers of
`ClusterClient.asScoped()` to control how the CPS `project_routing`
header is injected on a per-client basis.

### `asScoped` options

`ClusterClient.asScoped(request, opts?)` now accepts a typed `opts`
argument. Three named specializations of `AsScopedOptions` are provided,
each narrowing the `projectRouting` field:

- `OriginOnlyRouting` (`projectRouting: 'origin-only'`, default) —
preserves the existing behavior, injects `_alias:_origin`.
- `AllProjectsRouting` (`projectRouting: 'all'`) — injects `_alias:*`,
routing across all CPS-connected projects.
- `SpaceNPRERouting` (`projectRouting: 'space'`) — derives the NPRE from
the request URL and injects `kibana_space_${spaceId}_default`.

Using `SpaceNPRERouting` requires passing a `ScopeableUrlRequest`
(`KibanaRequest | UrlRequest`) as the first argument. This is enforced
by an overload on `asScoped`, so passing an incompatible `FakeRequest`
(without a `url`) is a compile-time error.

### New request types

- `UrlRequest` — a minimal `FakeRequest` with a `url: URL` property, for
use in non-HTTP contexts (e.g. background tasks) where no real
`KibanaRequest` is available but space-level routing is needed.
- `ScopeableUrlRequest` — `KibanaRequest | UrlRequest`, the type
accepted by the `SpaceNPRERouting` overload of `asScoped`. Route
handlers can pass their incoming `KibanaRequest` directly.

### `OnRequestHandlerFactory`

A mandatory factory (`OnRequestHandlerFactory`) is passed to the
`ClusterClient` constructor instead of a bare `OnRequestHandler`. The
factory is called lazily per scope, producing the right handler based on
the routing mode. The `projectRouting` field is a discriminated union: a
`ScopeableUrlRequest` signals space routing, while `'origin-only'` and
`'all'` cover the remaining modes.

### `getSpaceNPRE` in `@kbn/cps-server-utils`

Broadened to accept `string | { url: URL }` instead of `string |
KibanaRequest`, removing the coupling to `KibanaRequest`. Both
`KibanaRequest` and `UrlRequest` satisfy this structural type. A
defensive runtime guard is retained (with a documented `@throws`) to
protect against JavaScript callers bypassing the type system.

### Known limitation

`getSpaceNPRE` assumes the server base path is `/`. If Kibana were
deployed with a custom `server.basePath` the space segment would not be
stripped before matching, and the function would always resolve to the
default space. CPS is a Serverless-only feature and Serverless
deployments always run at the root path, so this is not a concern in
practice today. This assumption is documented in the JSDoc.

### CLI setup client

`cli_setup`'s `ClusterClient` is on-prem only; it uses
`getRequestHandlerFactory(false)` (CPS disabled), which strips any
`project_routing` header rather than no-oping.

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Rudolf Meijering <skaapgif@gmail.com>
bhapas pushed a commit to bhapas/kibana that referenced this pull request Feb 25, 2026
)

Part of elastic/kibana-team#2829

## Summary

Builds on elastic#254085 (`@kbn/cps-server-utils`) to allow callers of
`ClusterClient.asScoped()` to control how the CPS `project_routing`
header is injected on a per-client basis.

### `asScoped` options

`ClusterClient.asScoped(request, opts?)` now accepts a typed `opts`
argument. Three named specializations of `AsScopedOptions` are provided,
each narrowing the `projectRouting` field:

- `OriginOnlyRouting` (`projectRouting: 'origin-only'`, default) —
preserves the existing behavior, injects `_alias:_origin`.
- `AllProjectsRouting` (`projectRouting: 'all'`) — injects `_alias:*`,
routing across all CPS-connected projects.
- `SpaceNPRERouting` (`projectRouting: 'space'`) — derives the NPRE from
the request URL and injects `kibana_space_${spaceId}_default`.

Using `SpaceNPRERouting` requires passing a `ScopeableUrlRequest`
(`KibanaRequest | UrlRequest`) as the first argument. This is enforced
by an overload on `asScoped`, so passing an incompatible `FakeRequest`
(without a `url`) is a compile-time error.

### New request types

- `UrlRequest` — a minimal `FakeRequest` with a `url: URL` property, for
use in non-HTTP contexts (e.g. background tasks) where no real
`KibanaRequest` is available but space-level routing is needed.
- `ScopeableUrlRequest` — `KibanaRequest | UrlRequest`, the type
accepted by the `SpaceNPRERouting` overload of `asScoped`. Route
handlers can pass their incoming `KibanaRequest` directly.

### `OnRequestHandlerFactory`

A mandatory factory (`OnRequestHandlerFactory`) is passed to the
`ClusterClient` constructor instead of a bare `OnRequestHandler`. The
factory is called lazily per scope, producing the right handler based on
the routing mode. The `projectRouting` field is a discriminated union: a
`ScopeableUrlRequest` signals space routing, while `'origin-only'` and
`'all'` cover the remaining modes.

### `getSpaceNPRE` in `@kbn/cps-server-utils`

Broadened to accept `string | { url: URL }` instead of `string |
KibanaRequest`, removing the coupling to `KibanaRequest`. Both
`KibanaRequest` and `UrlRequest` satisfy this structural type. A
defensive runtime guard is retained (with a documented `@throws`) to
protect against JavaScript callers bypassing the type system.

### Known limitation

`getSpaceNPRE` assumes the server base path is `/`. If Kibana were
deployed with a custom `server.basePath` the space segment would not be
stripped before matching, and the function would always resolve to the
default space. CPS is a Serverless-only feature and Serverless
deployments always run at the root path, so this is not a concern in
practice today. This assumption is documented in the JSDoc.

### CLI setup client

`cli_setup`'s `ClusterClient` is on-prem only; it uses
`getRequestHandlerFactory(false)` (CPS disabled), which strips any
`project_routing` header rather than no-oping.

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Rudolf Meijering <skaapgif@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:Core Platform Core services: plugins, logging, config, saved objects, http, ES client, i18n, etc t// v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants