diff --git a/packages/app/package.json b/packages/app/package.json
index 79101dff70..9401ca89be 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -142,8 +142,7 @@
"@types/react": "*",
"@types/react-dom": "*",
"@types/zen-observable": "^0.8.0",
- "cross-env": "^7.0.0",
- "msw": "^1.0.0"
+ "cross-env": "^7.0.0"
},
"bundled": true
}
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 06851d6ac7..2639f2add8 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -108,7 +108,7 @@ import { DevToolsPage } from '@backstage/plugin-devtools';
import { customDevToolsPage } from './components/devtools/CustomDevToolsPage';
import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities';
import { NotificationsPage } from '@backstage/plugin-notifications';
-import { AppMode } from './AppMode';
+import { ExperimentalAppProtection } from '@backstage/plugin-auth-react';
const app = createApp({
apis,
@@ -283,10 +283,10 @@ export default app.createRoot(
-
+
{routes}
-
+
>,
);
diff --git a/plugins/auth-react/api-report.md b/plugins/auth-react/api-report.md
index 7f1489543b..d6b7d2e17f 100644
--- a/plugins/auth-react/api-report.md
+++ b/plugins/auth-react/api-report.md
@@ -18,6 +18,11 @@ export type CookieAuthRefreshProviderProps = {
children: ReactNode;
};
+// @public
+export function ExperimentalAppProtection(props: {
+ children: ReactNode;
+}): JSX.Element;
+
// @public
export function RedirectToRoot(): React_2.JSX.Element | null;
diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json
index 4e4989ea22..9de3aade21 100644
--- a/plugins/auth-react/package.json
+++ b/plugins/auth-react/package.json
@@ -44,7 +44,8 @@
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
- "@testing-library/user-event": "^14.0.0"
+ "@testing-library/user-event": "^14.0.0",
+ "msw": "^1.0.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
diff --git a/packages/app/src/AppMode.test.tsx b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx
similarity index 88%
rename from packages/app/src/AppMode.test.tsx
rename to plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx
index b110944e1e..2884400ea9 100644
--- a/packages/app/src/AppMode.test.tsx
+++ b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.test.tsx
@@ -24,14 +24,14 @@ import {
renderInTestApp,
setupRequestMockHandlers,
} from '@backstage/test-utils';
-import { AppMode } from './AppMode';
+import { ExperimentalAppProtection } from './ExperimentalAppProtection';
import {
configApiRef,
discoveryApiRef,
fetchApiRef,
} from '@backstage/core-plugin-api';
-describe('AppMode', () => {
+describe('ExperimentalAppProtection', () => {
const worker = setupServer();
setupRequestMockHandlers(worker);
@@ -62,7 +62,7 @@ describe('AppMode', () => {
[fetchApiRef, fetchApiMock],
]}
>
- Test content
+ Test content
,
);
expect(screen.getByTestId('progress')).toBeVisible();
@@ -79,7 +79,7 @@ describe('AppMode', () => {
[fetchApiRef, fetchApiMock],
]}
>
- Test content
+ Test content
,
);
await waitFor(() =>
@@ -98,7 +98,7 @@ describe('AppMode', () => {
[fetchApiRef, fetchApiMock],
]}
>
- Test content
+ Test content
,
);
await waitFor(() =>
@@ -109,7 +109,9 @@ describe('AppMode', () => {
it('should render the children also when the public index is available', async () => {
fetchApiMock.fetch.mockResolvedValueOnce({ ok: true });
- await renderInTestApp(Test content);
+ await renderInTestApp(
+ Test content,
+ );
await waitFor(() =>
expect(screen.getByText('Test content')).toBeInTheDocument(),
);
@@ -138,7 +140,7 @@ describe('AppMode', () => {
[discoveryApiRef, discoveryApiMock],
]}
>
- Test content
+ Test content
,
);
await waitFor(() =>
diff --git a/packages/app/src/AppMode.tsx b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx
similarity index 76%
rename from packages/app/src/AppMode.tsx
rename to plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx
index 2b11064c65..f76b287d97 100644
--- a/packages/app/src/AppMode.tsx
+++ b/plugins/auth-react/src/components/ExperimentalAppProtection/ExperimentalAppProtection.tsx
@@ -15,7 +15,6 @@
*/
import React, { ReactNode } from 'react';
-import useAsync from 'react-use/lib/useAsync';
import {
configApiRef,
fetchApiRef,
@@ -23,25 +22,34 @@ import {
useApp,
} from '@backstage/core-plugin-api';
import { CookieAuthRefreshProvider } from '@backstage/plugin-auth-react';
+import { useAsync, useMountEffect } from '@react-hookz/web';
-export function AppMode(props: { children: ReactNode }): JSX.Element {
+/**
+ * @public
+ * A provider that will protect the app when running in public experimental mode.
+ */
+export function ExperimentalAppProtection(props: {
+ children: ReactNode;
+}): JSX.Element {
const { children } = props;
const fetchApi = useApi(fetchApiRef);
const configApi = useApi(configApiRef);
const Components = useApp().getComponents();
- const { loading, error, value } = useAsync(async () => {
+ const [state, actions] = useAsync(async () => {
const baseUrl = configApi.getString('backend.baseUrl');
const response = await fetchApi.fetch(`${baseUrl}/public/index.html`);
return response.ok;
- }, [fetchApi, configApi]);
+ });
- if (loading) {
+ useMountEffect(actions.execute);
+
+ if (state.status === 'not-executed' || state.status === 'loading') {
return ;
}
// Request failed, or the public index is not available
- if (error || !value) {
+ if (state.status === 'error' || !state.result) {
return <>{children}>;
}
diff --git a/plugins/auth-react/src/components/ExperimentalAppProtection/index.ts b/plugins/auth-react/src/components/ExperimentalAppProtection/index.ts
new file mode 100644
index 0000000000..c29b1e438d
--- /dev/null
+++ b/plugins/auth-react/src/components/ExperimentalAppProtection/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 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 { ExperimentalAppProtection } from './ExperimentalAppProtection';
diff --git a/plugins/auth-react/src/components/index.ts b/plugins/auth-react/src/components/index.ts
index c744c9b0d8..a85fad95ae 100644
--- a/plugins/auth-react/src/components/index.ts
+++ b/plugins/auth-react/src/components/index.ts
@@ -19,3 +19,4 @@
export * from './RedirectToRoot';
export * from './CookieAuthRefreshProvider';
+export * from './ExperimentalAppProtection';
diff --git a/yarn.lock b/yarn.lock
index 2155432223..666548dd95 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5119,6 +5119,7 @@ __metadata:
"@testing-library/react": ^14.0.0
"@testing-library/user-event": ^14.0.0
"@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0
+ msw: ^1.0.0
peerDependencies:
react: ^16.13.1 || ^17.0.0 || ^18.0.0
languageName: unknown
@@ -27526,7 +27527,6 @@ __metadata:
"@vitejs/plugin-react": ^4.0.4
cross-env: ^7.0.0
history: ^5.0.0
- msw: ^1.0.0
react: ^18.0.2
react-dom: ^18.0.2
react-router: ^6.3.0