feat: allow dynamically enabling and disabling extensions

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2026-03-08 18:08:09 -04:00
committed by Patrik Oldsberg
parent 00381fa7b2
commit 25b7ddd664
23 changed files with 458 additions and 21 deletions
@@ -45,6 +45,7 @@
},
"dependencies": {
"@backstage/errors": "workspace:^",
"@backstage/filter-predicates": "workspace:^",
"@backstage/types": "workspace:^",
"@backstage/version-bridge": "workspace:^",
"zod": "^3.25.76",
@@ -14,6 +14,7 @@ import { ExtensionBlueprint as ExtensionBlueprint_2 } from '@backstage/frontend-
import { ExtensionBlueprintParams as ExtensionBlueprintParams_2 } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef as ExtensionDataRef_2 } from '@backstage/frontend-plugin-api';
import { ExtensionInput as ExtensionInput_2 } from '@backstage/frontend-plugin-api';
import { FilterPredicate } from '@backstage/filter-predicates';
import { JsonObject } from '@backstage/types';
import { JsonValue } from '@backstage/types';
import { JSX as JSX_2 } from 'react';
@@ -254,6 +255,8 @@ export interface AppNodeSpec {
// (undocumented)
readonly disabled: boolean;
// (undocumented)
readonly enabled?: FilterPredicate;
// (undocumented)
readonly extension: Extension<unknown, unknown>;
// (undocumented)
readonly id: string;
@@ -577,6 +580,7 @@ export type CreateExtensionBlueprintOptions<
attachTo: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<UOutput, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TInputs;
output: Array<UOutput>;
config?: {
@@ -663,6 +667,7 @@ export type CreateExtensionOptions<
attachTo: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<UOutput, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TInputs;
output: Array<UOutput>;
config?: {
@@ -1026,6 +1031,7 @@ export interface ExtensionBlueprint<
attachTo?: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<NonNullable<T['output']>, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
params: TParamsInput extends ExtensionBlueprintDefineParams
? TParamsInput
: T['params'] extends ExtensionBlueprintDefineParams
@@ -1061,6 +1067,7 @@ export interface ExtensionBlueprint<
UParentInputs
>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TExtraInputs & {
[KName in keyof T['inputs']]?: `Error: Input '${KName &
string}' is already defined in parent definition`;
@@ -1702,6 +1709,7 @@ export interface OverridableExtensionDefinition<
UParentInputs
>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TExtraInputs & {
[KName in keyof T['inputs']]?: `Error: Input '${KName &
string}' is already defined in parent definition`;
@@ -17,6 +17,7 @@
import { createApiRef } from '../system';
import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring';
import { ExtensionAttachTo } from '../../wiring/resolveExtensionDefinition';
import { FilterPredicate } from '@backstage/filter-predicates';
/**
* The specification for this {@link AppNode} in the {@link AppTree}.
@@ -32,6 +33,7 @@ export interface AppNodeSpec {
readonly attachTo: ExtensionAttachTo;
readonly extension: Extension<unknown, unknown>;
readonly disabled: boolean;
readonly enabled?: FilterPredicate;
readonly config?: unknown;
readonly plugin: FrontendPlugin;
}
@@ -36,6 +36,7 @@ import {
} from './createExtensionBlueprint';
import { FrontendPlugin } from './createFrontendPlugin';
import { FrontendModule } from './createFrontendModule';
import { FilterPredicate } from '@backstage/filter-predicates';
/**
* This symbol is used to pass parameter overrides from the extension override to the blueprint factory
@@ -174,6 +175,7 @@ export type CreateExtensionOptions<
attachTo: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<UOutput, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TInputs;
output: Array<UOutput>;
config?: {
@@ -255,6 +257,7 @@ export interface OverridableExtensionDefinition<
UParentInputs
>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TExtraInputs & {
[KName in keyof T['inputs']]?: `Error: Input '${KName &
string}' is already defined in parent definition`;
@@ -474,6 +477,7 @@ export function createExtension<
name: options.name,
attachTo: options.attachTo,
disabled: options.disabled ?? false,
enabled: options.enabled,
inputs: bindInputs(options.inputs, options.kind, options.name),
output: options.output,
configSchema,
@@ -551,6 +555,7 @@ export function createExtension<
attachTo: (overrideOptions.attachTo ??
options.attachTo) as ExtensionDefinitionAttachTo,
disabled: overrideOptions.disabled ?? options.disabled,
enabled: overrideOptions.enabled ?? options.enabled,
inputs: bindInputs(
{
...(options.inputs ?? {}),
@@ -36,6 +36,7 @@ import {
} from './resolveInputOverrides';
import { ExtensionDataContainer } from './types';
import { PageBlueprint } from '../blueprints/PageBlueprint';
import { FilterPredicate } from '@backstage/filter-predicates';
/**
* A function used to define a parameter mapping function in order to facilitate
@@ -114,6 +115,7 @@ export type CreateExtensionBlueprintOptions<
attachTo: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<UOutput, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TInputs;
output: Array<UOutput>;
config?: {
@@ -221,6 +223,7 @@ export interface ExtensionBlueprint<
attachTo?: ExtensionDefinitionAttachTo<UParentInputs> &
VerifyExtensionAttachTo<NonNullable<T['output']>, UParentInputs>;
disabled?: boolean;
enabled?: FilterPredicate;
params: TParamsInput extends ExtensionBlueprintDefineParams
? TParamsInput
: T['params'] extends ExtensionBlueprintDefineParams
@@ -261,6 +264,7 @@ export interface ExtensionBlueprint<
UParentInputs
>;
disabled?: boolean;
enabled?: FilterPredicate;
inputs?: TExtraInputs & {
[KName in keyof T['inputs']]?: `Error: Input '${KName &
string}' is already defined in parent definition`;
@@ -510,6 +514,7 @@ export function createExtensionBlueprint<
attachTo: (args.attachTo ??
options.attachTo) as ExtensionDefinitionAttachTo,
disabled: args.disabled ?? options.disabled,
enabled: args.enabled ?? options.enabled,
inputs: options.inputs,
output: options.output as ExtensionDataRef[],
config: options.config,
@@ -527,6 +532,7 @@ export function createExtensionBlueprint<
attachTo: (args.attachTo ??
options.attachTo) as ExtensionDefinitionAttachTo,
disabled: args.disabled ?? options.disabled,
enabled: args.enabled ?? options.enabled,
inputs: { ...args.inputs, ...options.inputs },
output: (args.output ?? options.output) as ExtensionDataRef[],
config:
@@ -28,6 +28,7 @@ import {
OpaqueExtensionDefinition,
OpaqueExtensionInput,
} from '@internal/frontend';
import { FilterPredicate } from '@backstage/filter-predicates';
/** @public */
export type ExtensionAttachTo = { id: string; input: string };
@@ -74,6 +75,7 @@ export type InternalExtension<TConfig, TConfigInput> = Extension<
}
| {
readonly version: 'v2';
readonly enabled?: FilterPredicate;
readonly inputs: { [inputName in string]: ExtensionInput };
readonly output: Array<ExtensionDataRef>;
factory(options: {