The permission backend previously resolved userInfo and minted a plugin
request token sequentially for every authorize request with user
credentials. On high-traffic endpoints this meant two serial internal
HTTP round-trips per request, even when the same user made many
requests in quick succession.
This change:
1. Adds a 5-second TTL cache to DefaultUserInfoService so that repeated
getUserInfo() calls for the same user return the cached result
without an HTTP call to the auth backend.
2. Parallelises the getUserInfo() and getPluginRequestToken() calls in
the permission backend's handleRequest via Promise.all, saving one
sequential round-trip on cache misses.
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
The mock credentials created by mockCredentials.none(), .user(), and
.service() were missing the internal version: 'v1' field. This caused
toInternalBackstageCredentials() to throw when used with mock
credentials in tests.
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Refactors all test files that use TestDatabases/TestCaches with
it.each(databases.eachSupportedId()) to instead use describe.each at
the outer level. This ensures that all tests for one database engine
complete before moving to the next, rather than interleaving engines
across individual tests. This reduces the number of concurrent database
connections and should help with test timeout issues in CI.
The TestDatabases.create() call is hoisted to module scope so the
describe.each can iterate over supported IDs at the top level.
40 files changed across packages/backend-defaults,
packages/backend-test-utils, and multiple plugins.
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Add container layout to Header sections so the title, actions, tags, metadata, and tabs align with surrounding page content.
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
Previously, a predicate like `{ kind: 'API', $not: { 'spec.type': 'dataset' } }`
was silently accepted — the `kind` check was dropped entirely because
`evaluateFilterPredicate` short-circuited on the `$not` operator. This
made the filter match any non-dataset entity regardless of kind, which
is incorrect. The correct form requires wrapping in `$all`.
Both the runtime evaluator and the zod validation schemas now reject
predicates that mix `$all`/`$any`/`$not` with other keys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
The "respects initialDelayDuration per worker" test had only a 50ms
margin (350ms total sleep vs 300ms initial delay) for the worker to
complete its internal DB roundtrips before asserting. Under CI load
this frequently wasn't enough. Replace the fixed sleep with
waitForExpect, consistent with the rest of the test file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
* feat(ui): add DatePicker component
Add a single-date picker built on React Aria's DatePicker, mirroring the
existing DateRangePicker implementation. Includes the date field with
segmented input, calendar popover, BUI design tokens, bg consumer
pattern, and full keyboard/screen reader accessibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Erik Hughes <erikh@spotify.com>
* fix(ui): address DatePicker PR feedback
- Remove unused dataAttributes spread from DatePickerGroup
- Mark DatePickerGroupDefinition and DatePickerCalendarDefinition as
public so CSS class name changes appear in API reports
- Add Affected components line to changeset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Erik Hughes <erikh@spotify.com>
* fix(ui): restore dataAttributes spread in DatePickerGroup
The bg: 'consumer' config on DatePickerGroupDefinition emits
data-on-bg attributes via useDefinition. Without spreading
dataAttributes onto <Group>, the CSS [data-on-bg] selectors
never match and background auto-increment doesn't work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Erik Hughes <erikh@spotify.com>
---------
Signed-off-by: Erik Hughes <erikh@spotify.com>
Co-authored-by: Erik Hughes <erikh@spotify.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
30.4.0 introduced synchronous require(ESM) that requires Node v24.9+,
breaking tests on Node 22. 30.3.0 pulls in @sinonjs/fake-timers@^15
which conflicts with @types/sinon@^17 (used by aws-sdk-client-mock).
30.2.0 avoids both issues cleanly.
Resets lockfile to a clean base-install from master constraints.
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the monorepo's own jest resolution consistent with the template
range, and update the lockfile to 30.3.0 accordingly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Jest 30.4.0 introduced synchronous require(ESM) support that requires
Node v24.9+. With ^30.2.0 in the templates, a fresh yarn install in
e2e test apps resolves to 30.4.x and breaks on Node 22.
Pin to ~30.3.0 (30.3.x patches only), which includes the 30.3.0 fix
that explicitly disables the require_module feature flag, while staying
below the 30.4.0 runtime requirement.
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
Add `position: relative` to `.bui-Table` so it becomes the containing
block for react-aria's absolutely positioned hidden checkbox inputs.
Without this, the inputs position themselves relative to a distant
ancestor, extending its scrollable area and creating phantom scroll
height.
Signed-off-by: Johan Persson <johanopersson@gmail.com>
Adds an optional `webIdentityTokenFile` field to
`AwsIntegrationAccountConfig` and `AwsIntegrationDefaultAccountConfig`.
When set on a per-account config along with a `roleName` and no static
credentials, `DefaultAwsCredentialsManager` now retrieves credentials
by calling `AssumeRoleWithWebIdentity` directly using the file's
contents as the web identity token (via `fromTokenFile`). The token
file is re-read on each refresh, so an external process can rotate it
in place — the same mechanism EKS IRSA uses, where the kubelet rotates
a projected service account token at the path identified by
`AWS_WEB_IDENTITY_TOKEN_FILE`.
This unlocks multi-account `AssumeRoleWithWebIdentity` for backends
running outside AWS (GKE, Cloud Run, Vault sidecars, etc.) without
requiring every plugin to construct a custom `AwsCredentialsManager`.
Existing call sites and configurations are unaffected — the new path
is opt-in via the new optional field.
Validator rejects:
- `webIdentityTokenFile` combined with static credentials
(`accessKeyId`/`secretAccessKey`) on the same account
- `webIdentityTokenFile` combined with `profile` on the same account
- `webIdentityTokenFile` without a `roleName` (matches the existing
precedent for `externalId`/`region`/`partition` without `roleName`)
- `webIdentityTokenFile` combined with `externalId` (the STS
`AssumeRoleWithWebIdentity` API does not accept an external ID)
Same rules apply at the `accountDefaults` level. The `!config.accessKeyId`
guard in `getSdkCredentialProvider` is defensive — it protects callers
that build an `AwsIntegrationAccountConfig` directly without going
through `readAwsIntegrationConfig`. In that case we fall through to the
existing static-creds AssumeRole path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Brian Hudson <brian.r.hudson@gmail.com>
Introduces a Combobox component to Backstage UI for times when you want to allow users to choose from a list of values but also specify their own in some cases.
---------
Signed-off-by: James Brooks <jamesbrooks@spotify.com>
Style the bui-Link itself when keyboard-focused using the ring token,
matching the established pattern from ButtonLink. Also tweak the
changeset wording to reflect the new behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Emma Indal <emma.indahl@gmail.com>
Card's focus-ring CSS keys off data-focus-visible on the
bui-CardTrigger, but Link (the trigger when href is set) wasn't
emitting it because useLink does not track focus-visible state.
Compose useFocusRing in Link so keyboard focus is now properly
indicated on Card href=... and any other Link styled via
[data-focus-visible].
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Emma Indal <emma.indahl@gmail.com>
Validate the parsed Retry-After value is a finite positive number before
using it as a delay, falling back to exponential backoff otherwise. This
prevents a near-zero delay tight retry loop when the header contains an
HTTP-date or unexpected value.
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Capturing globalThis.fetch as a value at construction time meant the
GitLabUrlReader tests bypassed the MSW fetch interceptor and hit the
real gitlab.com (returning 401). Wrapping the call resolves fetch at
invocation time so the patched fetch is used.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>