Merge pull request #19882 from backstage/mob/extensionInstances

frontend-app-api: Internal restructure
This commit is contained in:
Johan Haals
2023-09-12 10:50:25 +02:00
committed by GitHub
12 changed files with 70 additions and 57 deletions
+1 -1
View File
@@ -20,4 +20,4 @@
* @packageDocumentation
*/
export { createApp } from './createApp';
export * from './wiring';
@@ -20,10 +20,10 @@ import {
BackstagePlugin,
coreExtensionData,
} from '@backstage/frontend-plugin-api';
import { Core } from './extensions/Core';
import { CoreRoutes } from './extensions/CoreRoutes';
import { CoreLayout } from './extensions/CoreLayout';
import { CoreNav } from './extensions/CoreNav';
import { Core } from '../extensions/Core';
import { CoreRoutes } from '../extensions/CoreRoutes';
import { CoreLayout } from '../extensions/CoreLayout';
import { CoreNav } from '../extensions/CoreNav';
import {
createExtensionInstance,
ExtensionInstance,
@@ -32,8 +32,8 @@ import {
ExtensionInstanceParameters,
mergeExtensionParameters,
readAppExtensionParameters,
} from './wiring/parameters';
import { RoutingProvider } from './routing/RoutingContext';
} from './parameters';
import { RoutingProvider } from '../routing/RoutingContext';
import {
AnyApiFactory,
ApiHolder,
@@ -47,7 +47,7 @@ import {
BackstagePlugin as LegacyBackstagePlugin,
featureFlagsApiRef,
} from '@backstage/core-plugin-api';
import { getAvailablePlugins } from './wiring/discovery';
import { getAvailablePlugins } from './discovery';
import {
ApiFactoryRegistry,
ApiProvider,
@@ -57,22 +57,22 @@ import {
// TODO: Get rid of all of these
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { AppThemeProvider } from '../../core-app-api/src/app/AppThemeProvider';
import { AppThemeProvider } from '../../../core-app-api/src/app/AppThemeProvider';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { AppContextProvider } from '../../core-app-api/src/app/AppContext';
import { AppContextProvider } from '../../../core-app-api/src/app/AppContext';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { LocalStorageFeatureFlags } from '../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags';
import { LocalStorageFeatureFlags } from '../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';
import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseUrlConfigs';
import { overrideBaseUrlConfigs } from '../../../core-app-api/src/app/overrideBaseUrlConfigs';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import {
apis as defaultApis,
components as defaultComponents,
icons as defaultIcons,
themes as defaultThemes,
} from '../../app-defaults/src/defaults';
} from '../../../app-defaults/src/defaults';
import { BrowserRouter } from 'react-router-dom';
/** @public */
@@ -174,13 +174,8 @@ export function createApp(options: {
return {
createRoot() {
const rootComponents = rootInstances
.map(
e =>
e.data.get(
coreExtensionData.reactComponent.id,
) as typeof coreExtensionData.reactComponent.T,
)
.filter(Boolean);
.map(e => e.getData(coreExtensionData.reactComponent))
.filter((x): x is React.ComponentType => !!x);
return (
<ApiProvider apis={apiHolder}>
<AppContextProvider appContext={appContext}>
@@ -254,13 +249,8 @@ function createApiHolder(
const apiFactories =
coreExtension.attachments
.get('apis')
?.map(
e =>
e.data.get(
coreExtensionData.apiFactory.id,
) as typeof coreExtensionData.apiFactory.T,
)
.filter(Boolean) ?? [];
?.map(e => e.getData(coreExtensionData.apiFactory))
.filter((x): x is AnyApiFactory => !!x) ?? [];
for (const factory of apiFactories) {
factoryRegistry.register('default', factory);
@@ -307,10 +297,8 @@ export function extractRouteInfoFromInstanceTree(
const results = new Map<RouteRef, string>();
function visit(current: ExtensionInstance, basePath: string) {
const routePath = current.data.get(coreExtensionData.routePath.id) ?? '';
const routeRef = current.data.get(
coreExtensionData.routeRef.id,
) as RouteRef;
const routePath = current.getData(coreExtensionData.routePath) ?? '';
const routeRef = current.getData(coreExtensionData.routeRef);
// TODO: join paths in a more robust way
const fullPath = basePath + routePath;
@@ -14,21 +14,26 @@
* limitations under the License.
*/
import { BackstagePlugin, Extension } from '@backstage/frontend-plugin-api';
import {
BackstagePlugin,
Extension,
ExtensionDataRef,
} from '@backstage/frontend-plugin-api';
import mapValues from 'lodash/mapValues';
/** @internal */
export interface ExtensionInstance {
readonly $$type: '@backstage/ExtensionInstance';
readonly id: string;
/**
* Maps extension data ref IDs to extensions produced.
* Get concrete value for the given extension data reference. Returns undefined if no value is available.
*/
readonly data: Map<string, unknown>;
getData<T>(ref: ExtensionDataRef<T>): T | undefined;
/**
* Maps input names to the actual instances given to them.
*/
readonly attachments: Map<string, ExtensionInstance[]>;
readonly $$type: 'extension-instance';
}
/** @internal */
@@ -70,7 +75,7 @@ export function createExtensionInstance(options: {
({ extensionData: pointData }, inputName) => {
// TODO: validation
return (attachments.get(inputName) ?? []).map(attachment =>
mapValues(pointData, ref => attachment.data.get(ref.id)),
mapValues(pointData, ref => attachment.getData(ref)),
);
},
),
@@ -82,9 +87,12 @@ export function createExtensionInstance(options: {
}
return {
$$type: '@backstage/ExtensionInstance',
id: options.extension.id,
data: extensionData,
getData<T>(ref: ExtensionDataRef<T>): T | undefined {
return extensionData.get(ref.id) as T | undefined;
},
attachments,
$$type: 'extension-instance',
};
}
@@ -35,7 +35,7 @@ export function getAvailablePlugins(): BackstagePlugin[] {
function isBackstagePlugin(obj: unknown): obj is BackstagePlugin {
if (obj !== null && typeof obj === 'object' && '$$type' in obj) {
return obj.$$type === 'backstage-plugin';
return obj.$$type === '@backstage/BackstagePlugin';
}
return false;
}
@@ -0,0 +1,17 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createApp } from './createApp';
+3 -3
View File
@@ -24,7 +24,7 @@ export type AnyExtensionDataMap = {
// @public (undocumented)
export interface BackstagePlugin {
// (undocumented)
$$type: 'backstage-plugin';
$$type: '@backstage/BackstagePlugin';
// (undocumented)
extensions: Extension<unknown>[];
// (undocumented)
@@ -176,7 +176,7 @@ export function createSchemaFromZod<TOutput, TInput>(
// @public (undocumented)
export interface Extension<TConfig> {
// (undocumented)
$$type: 'extension';
$$type: '@backstage/Extension';
// (undocumented)
at: string;
// (undocumented)
@@ -268,7 +268,7 @@ export type ExtensionDataRef<
id: string;
T: TData;
config: TConfig;
$$type: 'extension-data';
$$type: '@backstage/ExtensionDataRef';
};
// @public (undocumented)
@@ -31,7 +31,7 @@ describe('createApiExtension', () => {
});
expect(extension).toEqual({
$$type: 'extension',
$$type: '@backstage/Extension',
id: 'apis.test',
at: 'core/apis',
disabled: false,
@@ -39,7 +39,7 @@ describe('createApiExtension', () => {
inputs: {},
output: {
api: expect.objectContaining({
$$type: 'extension-data',
$$type: '@backstage/ExtensionDataRef',
id: 'core.api.factory',
config: {},
}),
@@ -63,9 +63,9 @@ describe('createApiExtension', () => {
});
},
});
// boo
expect(extension).toEqual({
$$type: 'extension',
$$type: '@backstage/Extension',
id: 'apis.test',
at: 'core/apis',
disabled: false,
@@ -73,7 +73,7 @@ describe('createApiExtension', () => {
inputs: {},
output: {
api: expect.objectContaining({
$$type: 'extension-data',
$$type: '@backstage/ExtensionDataRef',
id: 'core.api.factory',
config: {},
}),
@@ -33,7 +33,7 @@ describe('createPageExtension', () => {
component: async () => <div />,
}),
).toEqual({
$$type: 'extension',
$$type: '@backstage/Extension',
id: 'test',
at: 'core.routes/routes',
configSchema: expect.anything(),
@@ -61,7 +61,7 @@ describe('createPageExtension', () => {
component: async () => <div />,
}),
).toEqual({
$$type: 'extension',
$$type: '@backstage/Extension',
id: 'test',
at: 'other/place',
configSchema: expect.anything(),
@@ -86,7 +86,7 @@ describe('createPageExtension', () => {
component: async () => <div />,
}),
).toEqual({
$$type: 'extension',
$$type: '@backstage/Extension',
id: 'test',
at: 'core.routes/routes',
configSchema: expect.anything(),
@@ -87,7 +87,7 @@ export function createExtension<
return {
...options,
disabled: options.disabled ?? false,
$$type: 'extension',
$$type: '@backstage/Extension',
inputs: options.inputs ?? {},
factory({ bind, config, inputs }) {
// TODO: Simplify this, but TS wouldn't infer the input type for some reason
@@ -22,7 +22,7 @@ export type ExtensionDataRef<
id: string;
T: TData;
config: TConfig;
$$type: 'extension-data';
$$type: '@backstage/ExtensionDataRef';
};
/** @public */
@@ -40,7 +40,7 @@ export function createExtensionDataRef<TData>(
): ConfigurableExtensionDataRef<TData> {
return {
id,
$$type: 'extension-data',
$$type: '@backstage/ExtensionDataRef',
config: {},
optional() {
return { ...this, config: { ...this.config, optional: true } };
@@ -24,7 +24,7 @@ export interface PluginOptions {
/** @public */
export interface BackstagePlugin {
$$type: 'backstage-plugin';
$$type: '@backstage/BackstagePlugin';
id: string;
extensions: Extension<unknown>[];
}
@@ -33,7 +33,7 @@ export interface BackstagePlugin {
export function createPlugin(options: PluginOptions): BackstagePlugin {
return {
...options,
$$type: 'backstage-plugin',
$$type: '@backstage/BackstagePlugin',
extensions: options.extensions ?? [],
};
}
@@ -26,7 +26,7 @@ export type AnyExtensionDataMap = {
/** @public */
export interface Extension<TConfig> {
$$type: 'extension';
$$type: '@backstage/Extension';
id: string;
at: string;
disabled: boolean;