diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index c2483b40ee..393ffe2631 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -49,6 +49,11 @@ import {
import { Entity } from '@backstage/catalog-model';
import { Button, Grid } from '@material-ui/core';
import { EmptyState } from '@backstage/core';
+import {
+ EmbeddedRouter as LighthouseRouter,
+ LastLighthouseAuditCard,
+ isPluginApplicableToEntity as isLighthouseAvailable,
+} from '@backstage/plugin-lighthouse/';
const CICDSwitcher = ({ entity }: { entity: Entity }) => {
// This component is just an example of how you can implement your company's logic in entity page.
@@ -115,6 +120,11 @@ const OverviewContent = ({ entity }: { entity: Entity }) => (
+ {isLighthouseAvailable(entity) && (
+
+
+
+ )}
);
@@ -165,6 +175,11 @@ const WebsiteEntityPage = ({ entity }: { entity: Entity }) => (
title="CI/CD"
element={}
/>
+ }
+ />
+ Boolean(entity.metadata.annotations?.[LIGHTHOUSE_WEBSITE_URL_ANNOTATION]) ||
+ entity.metadata.name === 'marketing-site';
export const Router = () => (
@@ -28,3 +35,14 @@ export const Router = () => (
} />
);
+
+export const EmbeddedRouter = () => (
+
+ } />
+ } />
+ }
+ />
+
+);
diff --git a/plugins/lighthouse/src/api.ts b/plugins/lighthouse/src/api.ts
index 5b6cd9aae7..03b42a38b9 100644
--- a/plugins/lighthouse/src/api.ts
+++ b/plugins/lighthouse/src/api.ts
@@ -104,6 +104,7 @@ export type LighthouseApi = {
getWebsiteList: (listOptions: LASListRequest) => Promise;
getWebsiteForAuditId: (auditId: string) => Promise;
triggerAudit: (payload: TriggerAuditPayload) => Promise;
+ getWebsiteByUrl: (websiteUrl: string) => Promise;
};
export const lighthouseApiRef = createApiRef({
@@ -150,4 +151,10 @@ export class LighthouseRestApi implements LighthouseApi {
},
});
}
+
+ async getWebsiteByUrl(websiteUrl: string): Promise {
+ return this.fetch(
+ `/v1/websites/${encodeURIComponent(websiteUrl)}`,
+ );
+ }
}
diff --git a/plugins/lighthouse/src/components/AuditList/AuditListForEntity.test.tsx b/plugins/lighthouse/src/components/AuditList/AuditListForEntity.test.tsx
new file mode 100644
index 0000000000..7f10914d9c
--- /dev/null
+++ b/plugins/lighthouse/src/components/AuditList/AuditListForEntity.test.tsx
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core';
+import {
+ lighthouseApiRef,
+ LighthouseRestApi,
+ WebsiteListResponse,
+} from '../../api';
+import mockFetch from 'jest-fetch-mock';
+
+import * as data from '../../__fixtures__/website-list-response.json';
+import { EntityContext } from '@backstage/plugin-catalog';
+import { Entity } from '@backstage/catalog-model';
+import { AuditListForEntity } from './AuditListForEntity';
+import { lightTheme } from '@backstage/theme';
+import { ThemeProvider } from '@material-ui/core';
+import { MemoryRouter } from 'react-router-dom';
+import { useWebsiteForEntity } from '../../hooks/useWebsiteForEntity';
+
+jest.mock('../../hooks/useWebsiteForEntity', () => ({
+ useWebsiteForEntity: jest.fn(),
+}));
+
+const websiteListResponse = data as WebsiteListResponse;
+const entityWebsite = websiteListResponse.items[0];
+
+describe('', () => {
+ let apis: ApiRegistry;
+
+ const mockErrorApi: jest.Mocked = {
+ post: jest.fn(),
+ error$: jest.fn(),
+ };
+
+ beforeEach(() => {
+ apis = ApiRegistry.from([
+ [lighthouseApiRef, new LighthouseRestApi('http://lighthouse')],
+ [errorApiRef, mockErrorApi],
+ ]);
+ mockFetch.mockResponse(JSON.stringify(entityWebsite));
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: entityWebsite,
+ loading: false,
+ error: null,
+ });
+ });
+
+ const entity: Entity = {
+ apiVersion: 'v1',
+ kind: 'Component',
+ metadata: {
+ name: 'software',
+ annotations: {
+ 'lighthouse.com/website-url': entityWebsite.url,
+ },
+ },
+ spec: {
+ owner: 'guest',
+ type: 'Website',
+ lifecycle: 'development',
+ },
+ };
+
+ const subject = (value = {}) =>
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ it('renders the audit list for the entity', async () => {
+ const { findByText } = subject();
+ expect(await findByText(entityWebsite.url)).toBeInTheDocument();
+ });
+
+ describe('where the data is loading', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: true,
+ error: null,
+ });
+ });
+
+ it('renders a Progress element', async () => {
+ const { findByTestId } = subject();
+ expect(await findByTestId('progress')).toBeInTheDocument();
+ });
+ });
+
+ describe('where there is an error loading data', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: false,
+ error: 'error',
+ });
+ });
+
+ it('renders nothing', async () => {
+ const { queryByTestId } = subject();
+ expect(await queryByTestId('AuditListTable')).toBeNull();
+ });
+ });
+
+ describe('where there is not data', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: false,
+ error: null,
+ });
+ });
+
+ it('renders nothing', async () => {
+ const { queryByTestId } = subject();
+ expect(await queryByTestId('AuditListTable')).toBeNull();
+ });
+ });
+});
diff --git a/plugins/lighthouse/src/components/AuditList/AuditListForEntity.tsx b/plugins/lighthouse/src/components/AuditList/AuditListForEntity.tsx
new file mode 100644
index 0000000000..616fd0fa8a
--- /dev/null
+++ b/plugins/lighthouse/src/components/AuditList/AuditListForEntity.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+import React from 'react';
+import { AuditListTable } from './AuditListTable';
+import { Progress } from '@backstage/core';
+import { useWebsiteForEntity } from '../../hooks/useWebsiteForEntity';
+
+export const AuditListForEntity = () => {
+ const { value, loading, error } = useWebsiteForEntity();
+ if (loading) {
+ return ;
+ }
+ if (error || !value) {
+ return null;
+ }
+
+ return ;
+};
diff --git a/plugins/lighthouse/src/components/AuditView/index.tsx b/plugins/lighthouse/src/components/AuditView/index.tsx
index d0e743a77f..54bd179418 100644
--- a/plugins/lighthouse/src/components/AuditView/index.tsx
+++ b/plugins/lighthouse/src/components/AuditView/index.tsx
@@ -124,7 +124,7 @@ const AuditView: FC<{ audit?: Audit }> = ({ audit }: { audit?: Audit }) => {
);
};
-const ConnectedAuditView: FC<{}> = () => {
+export const AuditViewContent: FC<{}> = () => {
const lighthouseApi = useApi(lighthouseApiRef);
const params = useParams() as { id: string };
const classes = useStyles();
@@ -173,32 +173,35 @@ const ConnectedAuditView: FC<{}> = () => {
}
return (
-
-
-
- navigate(`../../${createAuditButtonUrl}`)}
>
-
-
-
- {content}
-
-
+ Create New Audit
+
+
+
+ {content}
+ >
);
};
+const ConnectedAuditView = () => (
+
+
+
+
+
+
+);
+
export default ConnectedAuditView;
diff --git a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx
new file mode 100644
index 0000000000..20b3b9c072
--- /dev/null
+++ b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.test.tsx
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import {
+ AuditCompleted,
+ LighthouseCategoryId,
+ WebsiteListResponse,
+} from '../../api';
+import { EntityContext } from '@backstage/plugin-catalog';
+import { Entity } from '@backstage/catalog-model';
+import { LastLighthouseAuditCard } from './LastLighthouseAuditCard';
+import { lightTheme } from '@backstage/theme';
+import { ThemeProvider } from '@material-ui/core';
+import { useWebsiteForEntity } from '../../hooks/useWebsiteForEntity';
+import { MemoryRouter } from 'react-router-dom';
+import * as data from '../../__fixtures__/website-list-response.json';
+
+jest.mock('../../hooks/useWebsiteForEntity', () => ({
+ useWebsiteForEntity: jest.fn(),
+}));
+
+const websiteListResponse = data as WebsiteListResponse;
+let entityWebsite = websiteListResponse.items[2];
+
+describe('', () => {
+ const asPercentage = (fraction: number) => `${fraction * 100}%`;
+
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: entityWebsite,
+ loading: false,
+ error: null,
+ });
+ });
+
+ const entity: Entity = {
+ apiVersion: 'v1',
+ kind: 'Component',
+ metadata: {
+ name: 'software',
+ annotations: {
+ 'lighthouse.com/website-url': entityWebsite.url,
+ },
+ },
+ spec: {
+ owner: 'guest',
+ type: 'Website',
+ lifecycle: 'development',
+ },
+ };
+
+ const subject = (value = {}) =>
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ describe('where the last audit completed successfully', () => {
+ const audit = entityWebsite.lastAudit as AuditCompleted;
+
+ it('renders the performance data for the audit', async () => {
+ const { findByText } = subject();
+ expect(await findByText(audit.url)).toBeInTheDocument();
+ expect(await findByText(audit.status)).toBeInTheDocument();
+ for (const category of Object.keys(audit.categories)) {
+ const { score } = audit.categories[category as LighthouseCategoryId];
+ expect(await findByText(asPercentage(score))).toBeInTheDocument();
+ }
+ });
+
+ describe('where a category score is not a number', () => {
+ beforeEach(() => {
+ entityWebsite = { ...entityWebsite };
+ (entityWebsite.lastAudit as AuditCompleted).categories.accessibility.score = NaN;
+ });
+
+ afterEach(() => {
+ entityWebsite = websiteListResponse.items[2];
+ });
+
+ it('renders the performance data for the audit', async () => {
+ const { findByText } = subject();
+ expect(await findByText('N/A')).toBeInTheDocument();
+ });
+ });
+ });
+
+ describe('where the last audit is in running', () => {
+ const audit = websiteListResponse.items[0].lastAudit as AuditCompleted;
+
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: websiteListResponse.items[0],
+ loading: false,
+ error: null,
+ });
+ });
+
+ it('renders the url and status of the audit', async () => {
+ const { findByText } = subject();
+ expect(await findByText(audit.url)).toBeInTheDocument();
+ expect(await findByText(audit.status)).toBeInTheDocument();
+ });
+ });
+
+ describe('where the data is loading', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: true,
+ error: null,
+ });
+ });
+
+ it('renders a Progress element', async () => {
+ const { findByTestId } = subject();
+ expect(await findByTestId('progress')).toBeInTheDocument();
+ });
+ });
+
+ describe('where there is an error loading data', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: false,
+ error: 'error',
+ });
+ });
+
+ it('renders nothing', async () => {
+ const { queryByTestId } = subject();
+ expect(await queryByTestId('AuditListTable')).toBeNull();
+ });
+ });
+ //
+ describe('where there is no data', () => {
+ beforeEach(() => {
+ (useWebsiteForEntity as jest.Mock).mockReturnValue({
+ value: null,
+ loading: false,
+ error: null,
+ });
+ });
+
+ it('renders nothing', async () => {
+ const { queryByTestId } = subject();
+ expect(await queryByTestId('AuditListTable')).toBeNull();
+ });
+ });
+});
diff --git a/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.tsx b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.tsx
new file mode 100644
index 0000000000..d82e6c5c6c
--- /dev/null
+++ b/plugins/lighthouse/src/components/Cards/LastLighthouseAuditCard.tsx
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+import React, { FC } from 'react';
+import { Audit, AuditCompleted, LighthouseCategoryId } from '../../api';
+import {
+ InfoCard,
+ Progress,
+ StatusError,
+ StatusOK,
+ StatusWarning,
+ StructuredMetadataTable,
+} from '@backstage/core';
+import { useWebsiteForEntity } from '../../hooks/useWebsiteForEntity';
+import AuditStatusIcon from '../AuditStatusIcon';
+
+const LighthouseCategoryScoreStatus: FC<{ score: number }> = ({ score }) => {
+ const scoreAsPercentage = score * 100;
+ switch (true) {
+ case scoreAsPercentage >= 90:
+ return (
+ <>
+
+ {scoreAsPercentage}%
+ >
+ );
+ case scoreAsPercentage >= 50 && scoreAsPercentage < 90:
+ return (
+ <>
+
+ {scoreAsPercentage}%
+ >
+ );
+ case scoreAsPercentage < 50:
+ return (
+ <>
+
+ {scoreAsPercentage}%
+ >
+ );
+ default:
+ return N/A;
+ }
+};
+
+const LighthouseAuditStatus: FC<{ audit: Audit }> = ({ audit }) => (
+ <>
+
+ {audit.status.toUpperCase()}
+ >
+);
+
+const LighthouseAuditSummary: FC<{ audit: Audit }> = ({ audit }) => {
+ const { url } = audit;
+ const flattenedCategoryData: Record = {};
+ if (audit.status === 'COMPLETED') {
+ const categories = (audit as AuditCompleted).categories;
+ const categoryIds = Object.keys(categories) as LighthouseCategoryId[];
+ categoryIds.forEach((id: LighthouseCategoryId) => {
+ const { title, score } = categories[id];
+
+ flattenedCategoryData[title] = (
+
+ );
+ });
+ }
+ const tableData = {
+ url,
+ status: ,
+ ...flattenedCategoryData,
+ };
+
+ return ;
+};
+
+export const LastLighthouseAuditCard: FC<{}> = () => {
+ const { value: website, loading, error } = useWebsiteForEntity();
+
+ let content;
+ if (loading) {
+ content = ;
+ }
+ if (error) {
+ content = null;
+ }
+ if (website) {
+ content = ;
+ }
+ return {content};
+};
diff --git a/plugins/lighthouse/src/components/Cards/index.ts b/plugins/lighthouse/src/components/Cards/index.ts
new file mode 100644
index 0000000000..7595619808
--- /dev/null
+++ b/plugins/lighthouse/src/components/Cards/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 { LastLighthouseAuditCard } from './LastLighthouseAuditCard';
diff --git a/plugins/lighthouse/src/components/CreateAudit/index.tsx b/plugins/lighthouse/src/components/CreateAudit/index.tsx
index 36bd97485a..ad46b06020 100644
--- a/plugins/lighthouse/src/components/CreateAudit/index.tsx
+++ b/plugins/lighthouse/src/components/CreateAudit/index.tsx
@@ -53,7 +53,7 @@ const useStyles = makeStyles(theme => ({
},
}));
-const CreateAudit: FC<{}> = () => {
+export const CreateAuditContent: FC<{}> = () => {
const errorApi = useApi(errorApiRef);
const lighthouseApi = useApi(lighthouseApiRef);
const classes = useStyles();
@@ -94,88 +94,91 @@ const CreateAudit: FC<{}> = () => {
]);
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+ >
);
};
+const CreateAudit = () => (
+
+
+
+
+
+
+);
+
export default CreateAudit;
diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx
new file mode 100644
index 0000000000..868e26f04c
--- /dev/null
+++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+import React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core-api';
+import { lighthouseApiRef, WebsiteListResponse } from '../api';
+import { useWebsiteForEntity } from './useWebsiteForEntity';
+import { EntityContext } from '@backstage/plugin-catalog';
+import { Entity } from '@backstage/catalog-model';
+import * as data from '../__fixtures__/website-list-response.json';
+
+const websiteListResponse = data as WebsiteListResponse;
+const website = websiteListResponse.items[0];
+
+const mockErrorApi: jest.Mocked = {
+ post: jest.fn(),
+ error$: jest.fn(),
+};
+
+const mockLighthouseApi: jest.Mocked> = {
+ getWebsiteByUrl: jest.fn(),
+};
+
+describe('useWebsiteForEntity', () => {
+ const entity: Entity = {
+ apiVersion: 'v1',
+ kind: 'Component',
+ metadata: {
+ name: 'software',
+ annotations: {
+ 'lighthouse.com/website-url': website.url,
+ },
+ },
+ spec: {
+ owner: 'guest',
+ type: 'Website',
+ lifecycle: 'development',
+ },
+ };
+
+ const wrapper: React.FC<{}> = ({ children }) => {
+ return (
+
+
+ {children}
+
+
+ );
+ };
+
+ const subject = () =>
+ renderHook(useWebsiteForEntity, {
+ wrapper,
+ });
+
+ beforeEach(() => {
+ (mockLighthouseApi.getWebsiteByUrl as jest.Mock).mockResolvedValue(website);
+ });
+
+ it('returns the lighthouse information for the website url in annotations ', async () => {
+ const { result, waitForNextUpdate } = subject();
+ await waitForNextUpdate();
+ expect(result.current?.value).toBe(website);
+ });
+
+ describe('where there is an error', () => {
+ const error = new Error('useWebsiteForEntity unit test');
+
+ beforeEach(() => {
+ (mockLighthouseApi.getWebsiteByUrl as jest.Mock).mockRejectedValueOnce(
+ error,
+ );
+ });
+
+ it('posts the error to the error api and returns the error to the caller', async () => {
+ const { result, waitForNextUpdate } = subject();
+ await waitForNextUpdate();
+ expect(result.current?.error).toBe(error);
+ expect(mockErrorApi.post).toHaveBeenCalledWith(error);
+ });
+ });
+});
diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts b/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts
new file mode 100644
index 0000000000..c52e38f473
--- /dev/null
+++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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.
+ */
+import { useEntity } from '@backstage/plugin-catalog';
+import { LIGHTHOUSE_WEBSITE_URL_ANNOTATION } from '../../constants';
+import { errorApiRef, useApi } from '@backstage/core-api';
+import { lighthouseApiRef } from '../api';
+import { useAsync } from 'react-use';
+
+// For the sake of simplicity we assume that an entity has only one website url. This is to avoid encoding a list
+// type in an annotation which is a plain string.
+export const useWebsiteForEntity = () => {
+ const { entity } = useEntity();
+ const websiteUrl =
+ entity.metadata.annotations?.[LIGHTHOUSE_WEBSITE_URL_ANNOTATION] ?? '';
+ const lighthouseApi = useApi(lighthouseApiRef);
+ const errorApi = useApi(errorApiRef);
+ const response = useAsync(() => lighthouseApi.getWebsiteByUrl(websiteUrl), [
+ websiteUrl,
+ ]);
+ if (response.error) {
+ errorApi.post(response.error);
+ }
+ return response;
+};
diff --git a/plugins/lighthouse/src/index.ts b/plugins/lighthouse/src/index.ts
index bebdaaf713..64fe2f8cc0 100644
--- a/plugins/lighthouse/src/index.ts
+++ b/plugins/lighthouse/src/index.ts
@@ -15,5 +15,6 @@
*/
export { plugin } from './plugin';
-export { Router } from './Router';
+export { Router, isPluginApplicableToEntity, EmbeddedRouter } from './Router';
export * from './api';
+export * from './components/Cards';
diff --git a/yarn.lock b/yarn.lock
index a3b64f0650..832a5afc86 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4510,6 +4510,14 @@
"@babel/runtime" "^7.5.4"
"@types/testing-library__react-hooks" "^3.3.0"
+"@testing-library/react-hooks@^3.4.2":
+ version "3.4.2"
+ resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.4.2.tgz#8deb94f7684e0d896edd84a4c90e5b79a0810bc2"
+ integrity sha512-RfPG0ckOzUIVeIqlOc1YztKgFW+ON8Y5xaSPbiBkfj9nMkkiLhLeBXT5icfPX65oJV/zCZu4z8EVnUc6GY9C5A==
+ dependencies:
+ "@babel/runtime" "^7.5.4"
+ "@types/testing-library__react-hooks" "^3.4.0"
+
"@testing-library/react@^10.4.1":
version "10.4.3"
resolved "https://registry.npmjs.org/@testing-library/react/-/react-10.4.3.tgz#c6f356688cffc51f6b35385583d664bb11a161f4"
@@ -5622,6 +5630,13 @@
dependencies:
"@types/react-test-renderer" "*"
+"@types/testing-library__react-hooks@^3.4.0":
+ version "3.4.1"
+ resolved "https://registry.npmjs.org/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz#b8d7311c6c1f7db3103e94095fe901f8fef6e433"
+ integrity sha512-G4JdzEcq61fUyV6wVW9ebHWEiLK2iQvaBuCHHn9eMSbZzVh4Z4wHnUGIvQOYCCYeu5DnUtFyNYuAAgbSaO/43Q==
+ dependencies:
+ "@types/react-test-renderer" "*"
+
"@types/through@*":
version "0.0.30"
resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"