chore: move the appIdentityProxy across
Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Johan Haals <johan.haals@gmail.com> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -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 = () => (
|
||||
<InternalAppContext.Provider
|
||||
value={{ appIdentityProxy, routeObjects: routeInfo.routeObjects }}
|
||||
>
|
||||
{rootEl}
|
||||
</InternalAppContext.Provider>
|
||||
);
|
||||
const AppComponent = () => rootEl;
|
||||
|
||||
return {
|
||||
createRoot() {
|
||||
|
||||
@@ -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(
|
||||
<AppRouter
|
||||
appIdentityProxy={appIdentityProxy}
|
||||
SignInPageComponent={inputs.signInPage?.get(
|
||||
SignInPageBlueprint.dataRefs.component,
|
||||
)}
|
||||
@@ -126,12 +158,33 @@ function SignInPageWrapper({
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
type AppIdentityProxy = IdentityApi & {
|
||||
enableCookieAuth(ctx: {
|
||||
errorApi: ErrorApi;
|
||||
fetchApi: FetchApi;
|
||||
discoveryApi: DiscoveryApi;
|
||||
}): void;
|
||||
setTarget(
|
||||
impl: IdentityApi & /* backwards compat stuff */ {
|
||||
getUserId?(): string;
|
||||
getIdToken?(): Promise<string | undefined>;
|
||||
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<SignInPageProps>;
|
||||
RouterComponent?: ComponentType<PropsWithChildren<{}>>;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user