diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index cc1ae8bedc..34fc5c470e 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs';
import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions';
import { Router as SentryRouter } from '@backstage/plugin-sentry';
import React from 'react';
import {
+ AboutCard,
EntityPageLayout,
useEntity,
- AboutCard,
} from '@backstage/plugin-catalog';
import { Entity } from '@backstage/catalog-model';
import { Grid } from '@material-ui/core';
@@ -49,6 +50,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => (
title="Sentry"
element={}
/>
+ }
+ />
);
diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs
index c80d9c92d8..a52810b31c 100644
--- a/packages/create-app/templates/default-app/packages/app/package.json.hbs
+++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs
@@ -8,6 +8,7 @@
"@material-ui/icons": "^4.9.1",
"@backstage/cli": "^{{version}}",
"@backstage/core": "^{{version}}",
+ "@backstage/plugin-api-docs": "^{{version}}",
"@backstage/plugin-catalog": "^{{version}}",
"@backstage/plugin-register-component": "^{{version}}",
"@backstage/plugin-scaffolder": "^{{version}}",
diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx
index 4087789deb..be22003f4f 100644
--- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { Router as ApiDocsRouter } from '@backstage/plugin-api-docs';
import { Router as GitHubActionsRouter } from '@backstage/plugin-github-actions';
import React from 'react';
import {
@@ -38,6 +39,11 @@ const ServiceEntityPage = ({ entity }: { entity: Entity }) => (
title="CI/CD"
element={}
/>
+ }
+ />
);
diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json
index 644c6ed63a..b22cb6fd63 100644
--- a/plugins/api-docs/package.json
+++ b/plugins/api-docs/package.json
@@ -31,6 +31,7 @@
"@material-ui/lab": "4.0.0-alpha.45",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-router": "6.0.0-beta.0",
"react-router-dom": "6.0.0-beta.0",
"react-use": "^15.3.3",
"swagger-ui-react": "^3.31.1"
diff --git a/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx b/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx
new file mode 100644
index 0000000000..da2cf71ac4
--- /dev/null
+++ b/plugins/api-docs/src/catalog/EntityPageApi/EntityPageApi.tsx
@@ -0,0 +1,47 @@
+/*
+ * 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 { ComponentEntity, Entity } from '@backstage/catalog-model';
+import { Progress } from '@backstage/core';
+import React, { FC } from 'react';
+import { Grid } from '@material-ui/core';
+import {
+ ApiDefinitionCard,
+ useComponentApiEntities,
+ useComponentApiNames,
+} from '../../components';
+
+export const EntityPageApi: FC<{ entity: Entity }> = ({ entity }) => {
+ const apiNames = useComponentApiNames(entity as ComponentEntity);
+
+ const { apiEntities, loading } = useComponentApiEntities({
+ entity: entity as ComponentEntity,
+ });
+
+ if (loading) {
+ return ;
+ }
+
+ return (
+
+ {apiNames.map(api => (
+
+
+
+ ))}
+
+ );
+};
diff --git a/plugins/api-docs/src/catalog/EntityPageApi/index.ts b/plugins/api-docs/src/catalog/EntityPageApi/index.ts
new file mode 100644
index 0000000000..1d382e01de
--- /dev/null
+++ b/plugins/api-docs/src/catalog/EntityPageApi/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { EntityPageApi } from './EntityPageApi';
diff --git a/plugins/api-docs/src/catalog/Router.tsx b/plugins/api-docs/src/catalog/Router.tsx
new file mode 100644
index 0000000000..71640954b3
--- /dev/null
+++ b/plugins/api-docs/src/catalog/Router.tsx
@@ -0,0 +1,42 @@
+/*
+ * 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 { Entity } from '@backstage/catalog-model';
+import { Route, Routes } from 'react-router';
+import { WarningPanel } from '@backstage/core';
+import { catalogRoute } from '../routes';
+import { EntityPageApi } from './EntityPageApi';
+
+const isPluginApplicableToEntity = (entity: Entity) => {
+ return ((entity.spec?.implementsApis as string[]) || []).length > 0;
+};
+
+export const Router = ({ entity }: { entity: Entity }) =>
+ // TODO(shmidt-i): move warning to a separate standardized component
+ !isPluginApplicableToEntity(entity) ? (
+
+ The entity doesn't implement any APIs.
+
+ ) : (
+
+ }
+ />
+ )
+
+ );
diff --git a/plugins/api-docs/src/catalog/index.ts b/plugins/api-docs/src/catalog/index.ts
new file mode 100644
index 0000000000..4c177df914
--- /dev/null
+++ b/plugins/api-docs/src/catalog/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { Router } from './Router';
diff --git a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx
index 19cbaf6038..78afa51937 100644
--- a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx
+++ b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.test.tsx
@@ -16,7 +16,6 @@
import { Entity } from '@backstage/catalog-model';
import { ApiProvider, ApiRegistry, storageApiRef } from '@backstage/core';
-// TODO: Circular ref!
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog';
import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils';
import { render } from '@testing-library/react';
diff --git a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx
index 4f3b2b200d..cfeb71f7b7 100644
--- a/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx
+++ b/plugins/api-docs/src/components/ApiCatalogPage/ApiCatalogPage.tsx
@@ -15,7 +15,6 @@
*/
import { Content, useApi } from '@backstage/core';
-// TODO: Circular ref
import { catalogApiRef } from '@backstage/plugin-catalog';
import React from 'react';
import { useAsync } from 'react-use';
diff --git a/plugins/api-docs/src/components/index.ts b/plugins/api-docs/src/components/index.ts
new file mode 100644
index 0000000000..d49303c24a
--- /dev/null
+++ b/plugins/api-docs/src/components/index.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 { ApiDefinitionCard } from './ApiDefinitionCard';
+export { useComponentApiNames } from './useComponentApiNames';
+export { useComponentApiEntities } from './useComponentApiEntities';
diff --git a/plugins/api-docs/src/components/useComponentApiEntities.ts b/plugins/api-docs/src/components/useComponentApiEntities.ts
new file mode 100644
index 0000000000..11b5de988f
--- /dev/null
+++ b/plugins/api-docs/src/components/useComponentApiEntities.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { useAsyncRetry } from 'react-use';
+import { errorApiRef, useApi } from '@backstage/core';
+import { ApiEntity, ComponentEntity } from '@backstage/catalog-model';
+import { catalogApiRef } from '@backstage/plugin-catalog';
+import { useComponentApiNames } from './useComponentApiNames';
+
+export function useComponentApiEntities({
+ entity,
+}: {
+ entity: ComponentEntity;
+}): {
+ loading: boolean;
+ apiEntities?: Map;
+ error?: Error;
+ retry: () => void;
+} {
+ const catalogApi = useApi(catalogApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const apiNames = useComponentApiNames(entity);
+
+ const { loading, value: apiEntities, retry, error } = useAsyncRetry<
+ Map
+ >(async () => {
+ const resultMap = new Map();
+
+ await Promise.all(
+ apiNames.map(async name => {
+ try {
+ const api = (await catalogApi.getEntityByName({
+ kind: 'API',
+ name,
+ })) as ApiEntity | undefined;
+
+ if (api) {
+ resultMap.set(api.metadata.name, api);
+ }
+ } catch (e) {
+ errorApi.post(e);
+ }
+ }),
+ );
+
+ return resultMap;
+ }, [catalogApi, entity]);
+
+ return {
+ apiEntities,
+ loading,
+ error,
+ retry,
+ };
+}
diff --git a/plugins/api-docs/src/components/useComponentApiNames.ts b/plugins/api-docs/src/components/useComponentApiNames.ts
new file mode 100644
index 0000000000..0eabe2b6c7
--- /dev/null
+++ b/plugins/api-docs/src/components/useComponentApiNames.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { ComponentEntity } from '@backstage/catalog-model';
+
+export const useComponentApiNames = (entity: ComponentEntity) => {
+ return (entity.spec?.implementsApis as string[]) || [];
+};
diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts
index c82a2c263b..958355f063 100644
--- a/plugins/api-docs/src/index.ts
+++ b/plugins/api-docs/src/index.ts
@@ -14,5 +14,5 @@
* limitations under the License.
*/
-export { ApiDefinitionCard } from './components/ApiDefinitionCard/ApiDefinitionCard';
+export { Router } from './catalog';
export { plugin } from './plugin';
diff --git a/plugins/api-docs/src/routes.ts b/plugins/api-docs/src/routes.ts
index 48c530d4b5..eea911dd62 100644
--- a/plugins/api-docs/src/routes.ts
+++ b/plugins/api-docs/src/routes.ts
@@ -28,3 +28,8 @@ export const entityRoute = createRouteRef({
path: '/api-docs/:optionalNamespaceAndName/',
title: 'API',
});
+export const catalogRoute = createRouteRef({
+ icon: NoIcon,
+ path: '',
+ title: 'API',
+});
diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json
index e393be9d17..5f17fc2a80 100644
--- a/plugins/catalog/package.json
+++ b/plugins/catalog/package.json
@@ -23,7 +23,6 @@
"dependencies": {
"@backstage/catalog-model": "^0.1.1-alpha.21",
"@backstage/core": "^0.1.1-alpha.21",
- "@backstage/plugin-api-docs": "^0.1.1-alpha.21",
"@backstage/plugin-github-actions": "^0.1.1-alpha.21",
"@backstage/plugin-jenkins": "^0.1.1-alpha.21",
"@backstage/plugin-scaffolder": "^0.1.1-alpha.21",
diff --git a/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx b/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx
deleted file mode 100644
index 5c0ff5485b..0000000000
--- a/plugins/catalog/src/components/EntityPageApi/EntityPageApi.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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 { ApiEntityV1alpha1, Entity } from '@backstage/catalog-model';
-import { Content, Progress, useApi } from '@backstage/core';
-import { ApiDefinitionCard } from '@backstage/plugin-api-docs';
-import { Grid } from '@material-ui/core';
-import { Alert } from '@material-ui/lab';
-import React, { FC } from 'react';
-import { useAsync } from 'react-use';
-import { catalogApiRef } from '../..';
-
-export const EntityPageApi: FC<{ entity: Entity }> = ({ entity }) => {
- const catalogApi = useApi(catalogApiRef);
-
- const { value: apiEntities, loading } = useAsync(async () => {
- const a = await Promise.all(
- ((entity?.spec?.implementsApis as string[]) || []).map(api =>
- catalogApi.getEntityByName({
- kind: 'API',
- name: api,
- }),
- ),
- );
- const b = new Map();
-
- a.filter(api => !!api).forEach(api => {
- b.set(api?.metadata?.name!, api as ApiEntityV1alpha1);
- });
- return b;
- }, [catalogApi, entity]);
-
- return (
-
- {loading && }
- {!loading && (
-
- {((entity?.spec?.implementsApis as string[]) || []).map(api => {
- const apiEntity = apiEntities && apiEntities.get(api);
-
- return (
-
- {!apiEntity && (
-
- Error on fetching the API: {api}
-
- )}
-
- {apiEntity && (
-
- )}
-
- );
- })}
-
- )}
-
- );
-};