diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx
index 51d824ba67..537c6c1d91 100644
--- a/packages/frontend-app-api/src/wiring/createApp.tsx
+++ b/packages/frontend-app-api/src/wiring/createApp.tsx
@@ -42,18 +42,12 @@ import {
configApiRef,
featureFlagsApiRef,
identityApiRef,
- errorApiRef,
- discoveryApiRef,
- fetchApiRef,
} from '@backstage/core-plugin-api';
import { getAvailableFeatures } from './discovery';
import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';
// TODO: Get rid of all of these
-// eslint-disable-next-line @backstage/no-relative-monorepo-imports
-import { isProtectedApp } from '../../../core-app-api/src/app/isProtectedApp';
-// eslint-disable-next-line @backstage/no-relative-monorepo-imports
-import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';
+
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
@@ -67,7 +61,6 @@ import { CreateAppRouteBinder } from '../routing';
import { RouteResolver } from '../routing/RouteResolver';
import { resolveRouteBindings } from '../routing/resolveRouteBindings';
import { collectRouteIds } from '../routing/collectRouteIds';
-import { InternalAppContext } from './InternalAppContext';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
@@ -81,6 +74,9 @@ import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';
import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';
+// eslint-disable-next-line @backstage/no-relative-monorepo-imports
+import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';
+import { BackstageRouteObject } from '../routing/types';
function deduplicateFeatures(
allFeatures: FrontendFeature[],
@@ -215,6 +211,7 @@ class AppTreeApiProxy implements AppTreeApi {
// Helps delay callers from reaching out to the API before the app tree has been materialized
class RouteResolutionApiProxy implements RouteResolutionApi {
#delegate: RouteResolutionApi | undefined;
+ #routeObjects: BackstageRouteObject[] | undefined;
constructor(
private readonly tree: AppTree,
@@ -251,9 +248,14 @@ class RouteResolutionApiProxy implements RouteResolutionApi {
this.routeBindings,
this.basePath,
);
+ this.#routeObjects = routeInfo.routeObjects;
return routeInfo;
}
+
+ getRouteObjects() {
+ return this.#routeObjects;
+ }
}
/**
@@ -285,7 +287,6 @@ export function createSpecializedApp(options?: {
);
const factories = createApiFactories({ tree });
-
const appTreeApi = new AppTreeApiProxy(tree);
const routeResolutionApi = new RouteResolutionApiProxy(
tree,
@@ -312,25 +313,9 @@ export function createSpecializedApp(options?: {
instantiateAppNodeTree(appNode, apiHolder);
}
- const routeInfo = routeResolutionApi.initialize();
+ routeResolutionApi.initialize();
appTreeApi.initialize();
- if (isProtectedApp()) {
- const discoveryApi = apiHolder.get(discoveryApiRef);
- const errorApi = apiHolder.get(errorApiRef);
- const fetchApi = apiHolder.get(fetchApiRef);
- if (!discoveryApi || !errorApi || !fetchApi) {
- throw new Error(
- 'App is running in protected mode but missing required APIs',
- );
- }
- appIdentityProxy.enableCookieAuth({
- discoveryApi,
- errorApi,
- fetchApi,
- });
- }
-
const featureFlagApi = apiHolder.get(featureFlagsApiRef);
if (featureFlagApi) {
for (const feature of features) {
@@ -354,13 +339,7 @@ export function createSpecializedApp(options?: {
.get('app')![0]
.instance!.getData(coreExtensionData.reactElement);
- const AppComponent = () => (
-
- {rootEl}
-
- );
+ const AppComponent = () => rootEl;
return {
createRoot() {
diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx
index e54699fac5..9906480ad6 100644
--- a/plugins/app/src/extensions/AppRoot.tsx
+++ b/plugins/app/src/extensions/AppRoot.tsx
@@ -19,7 +19,6 @@ import React, {
Fragment,
PropsWithChildren,
ReactNode,
- useContext,
useState,
} from 'react';
import {
@@ -27,19 +26,26 @@ import {
RouterBlueprint,
SignInPageBlueprint,
coreExtensionData,
+ discoveryApiRef,
+ fetchApiRef,
+ errorApiRef,
createExtension,
createExtensionInput,
+ routeResolutionApiRef,
} from '@backstage/frontend-plugin-api';
import {
+ DiscoveryApi,
+ ErrorApi,
+ FetchApi,
IdentityApi,
+ ProfileInfo,
SignInPageProps,
configApiRef,
+ identityApiRef,
useApi,
} from '@backstage/core-plugin-api';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
-import { InternalAppContext } from '../../../../packages/frontend-app-api/src/wiring/InternalAppContext';
-// eslint-disable-next-line @backstage/no-relative-monorepo-imports
-import { AppIdentityProxy } from '../../../../packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';
+import { isProtectedApp } from '../../../../packages/core-app-api/src/app/isProtectedApp';
import { BrowserRouter } from 'react-router-dom';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { RouteTracker } from '../../../../packages/frontend-app-api/src/routing/RouteTracker';
@@ -68,7 +74,32 @@ export const AppRoot = createExtension({
]),
},
output: [coreExtensionData.reactElement],
- factory({ inputs }) {
+ factory({ inputs, apis }) {
+ const identityApi = apis.get(identityApiRef);
+ if (!identityApi) {
+ throw new Error('App requires an Identity API implementation');
+ }
+ if (!('enableCookieAuth' in identityApi)) {
+ throw new Error('Unexpected Identity API implementation');
+ }
+ const appIdentityProxy = identityApi as AppIdentityProxy;
+
+ if (isProtectedApp()) {
+ const discoveryApi = apis.get(discoveryApiRef);
+ const errorApi = apis.get(errorApiRef);
+ const fetchApi = apis.get(fetchApiRef);
+ if (!discoveryApi || !errorApi || !fetchApi) {
+ throw new Error(
+ 'App is running in protected mode but missing required APIs',
+ );
+ }
+ appIdentityProxy.enableCookieAuth({
+ discoveryApi,
+ errorApi,
+ fetchApi,
+ });
+ }
+
let content: React.ReactNode = (
<>
{inputs.elements.map(el => (
@@ -88,6 +119,7 @@ export const AppRoot = createExtension({
return [
coreExtensionData.reactElement(
{children}>;
}
+type AppIdentityProxy = IdentityApi & {
+ enableCookieAuth(ctx: {
+ errorApi: ErrorApi;
+ fetchApi: FetchApi;
+ discoveryApi: DiscoveryApi;
+ }): void;
+ setTarget(
+ impl: IdentityApi & /* backwards compat stuff */ {
+ getUserId?(): string;
+ getIdToken?(): Promise;
+ getProfile?(): ProfileInfo;
+ },
+ options: { signOutTargetUrl: string },
+ ): void;
+};
+
+type RouteResolverProxy = {
+ getRouteObjects(): any[];
+};
+
/**
* Props for the {@link AppRouter} component.
* @public
*/
export interface AppRouterProps {
children?: ReactNode;
+ appIdentityProxy: AppIdentityProxy;
SignInPageComponent?: ComponentType;
RouterComponent?: ComponentType>;
}
@@ -155,17 +208,22 @@ function DefaultRouter(props: PropsWithChildren<{}>) {
export function AppRouter(props: AppRouterProps) {
const {
children,
+ appIdentityProxy,
SignInPageComponent,
RouterComponent = DefaultRouter,
} = props;
const configApi = useApi(configApiRef);
+ const routeResolutionsApi = useApi(routeResolutionApiRef);
const basePath = getBasePath(configApi);
- const internalAppContext = useContext(InternalAppContext);
- if (!internalAppContext) {
- throw new Error('AppRouter must be rendered within the AppProvider');
+
+ // TODO: Private access for now, probably replace with path -> node lookup method on the API
+ if (!('getRouteObjects' in routeResolutionsApi)) {
+ throw new Error('Unexpected route resolution API implementation');
}
- const { routeObjects, appIdentityProxy } = internalAppContext;
+ const routeObjects = (
+ routeResolutionsApi as RouteResolverProxy
+ ).getRouteObjects();
// If the app hasn't configured a sign-in page, we just continue as guest.
if (!SignInPageComponent) {