diff --git a/plugins/api-docs/.eslintrc.js b/plugins/api-docs/.eslintrc.js
new file mode 100644
index 0000000000..13573efa9c
--- /dev/null
+++ b/plugins/api-docs/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: [require.resolve('@backstage/cli/config/eslint')],
+};
diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md
new file mode 100644
index 0000000000..7ea87004c3
--- /dev/null
+++ b/plugins/api-docs/README.md
@@ -0,0 +1,9 @@
+# API Documentation
+
+WORK IN PROGRESS
+
+This is an extension for the catalog plugin that provides components to discover and display API entities.
+
+## Links
+
+- (The Backstage homepage)[https://backstage.io]
diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx
new file mode 100644
index 0000000000..812a5585d4
--- /dev/null
+++ b/plugins/api-docs/dev/index.tsx
@@ -0,0 +1,20 @@
+/*
+ * 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 { createDevApp } from '@backstage/dev-utils';
+import { plugin } from '../src/plugin';
+
+createDevApp().registerPlugin(plugin).render();
diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json
new file mode 100644
index 0000000000..9b7f519f59
--- /dev/null
+++ b/plugins/api-docs/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "@backstage/plugin-api-docs",
+ "version": "0.1.1-alpha.18",
+ "main": "src/index.ts",
+ "types": "src/index.ts",
+ "license": "Apache-2.0",
+ "private": true,
+ "publishConfig": {
+ "access": "public",
+ "main": "dist/index.esm.js",
+ "types": "dist/index.d.ts"
+ },
+ "scripts": {
+ "build": "backstage-cli plugin:build",
+ "start": "backstage-cli plugin:serve",
+ "lint": "backstage-cli lint",
+ "test": "backstage-cli test",
+ "diff": "backstage-cli plugin:diff",
+ "prepack": "backstage-cli prepack",
+ "postpack": "backstage-cli postpack",
+ "clean": "backstage-cli clean"
+ },
+ "dependencies": {
+ "@backstage/catalog-model": "^0.1.1-alpha.18",
+ "@backstage/core": "^0.1.1-alpha.18",
+ "@backstage/plugin-catalog": "^0.1.1-alpha.18",
+ "@backstage/theme": "^0.1.1-alpha.18",
+ "@material-icons/font": "^1.0.2",
+ "@material-ui/core": "^4.9.1",
+ "@material-ui/icons": "^4.9.1",
+ "@material-ui/lab": "4.0.0-alpha.45",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "6.0.0-beta.0",
+ "react-use": "^15.3.3",
+ "swagger-ui-react": "^3.31.1"
+ },
+ "devDependencies": {
+ "@backstage/cli": "^0.1.1-alpha.18",
+ "@backstage/dev-utils": "^0.1.1-alpha.18",
+ "@backstage/test-utils": "^0.1.1-alpha.18",
+ "@testing-library/jest-dom": "^5.10.1",
+ "@testing-library/react": "^10.4.1",
+ "@testing-library/user-event": "^12.0.7",
+ "@types/jest": "^26.0.7",
+ "@types/node": "^12.0.0",
+ "@types/swagger-ui-react": "^3.23.2",
+ "jest-fetch-mock": "^3.0.3"
+ },
+ "files": [
+ "dist"
+ ]
+}
diff --git a/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx
new file mode 100644
index 0000000000..af101f0194
--- /dev/null
+++ b/plugins/api-docs/src/components/ApiDefinitionCard/ApiDefinitionCard.tsx
@@ -0,0 +1,34 @@
+/*
+ * 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 } from '@backstage/catalog-model';
+import { InfoCard } from '@backstage/core';
+import React, { FC } from 'react';
+import { ApiDefinitionWidget } from '../ApiDefinitionWidget/ApiDefinitionWidget';
+
+export const ApiDefinitionCard: FC<{
+ title?: string;
+ apiEntity: ApiEntityV1alpha1;
+}> = ({ title, apiEntity }) => {
+ const type = apiEntity?.spec?.type || '';
+ const definition = apiEntity?.spec?.definition || '';
+
+ return (
+
+
+
+ );
+};
diff --git a/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx b/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx
new file mode 100644
index 0000000000..ac54793b6d
--- /dev/null
+++ b/plugins/api-docs/src/components/ApiDefinitionWidget/ApiDefinitionWidget.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { OpenApiDefinitionWidget } from '../OpenApiDefinitionWidget/OpenApiDefinitionWidget';
+import { PlainApiDefinitionWidget } from '../PlainApiDefinitionWidget/PlainApiDefinitionWidget';
+
+export const ApiDefinitionWidget: FC<{
+ type: string;
+ definition: string;
+}> = ({ type, definition }) => {
+ switch (type) {
+ case 'openapi':
+ return ;
+
+ default:
+ return ;
+ }
+};
diff --git a/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.test.tsx b/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.test.tsx
new file mode 100644
index 0000000000..4bb39b6e8c
--- /dev/null
+++ b/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.test.tsx
@@ -0,0 +1,34 @@
+/*
+ * 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 { lightTheme } from '@backstage/theme';
+import { ThemeProvider } from '@material-ui/core';
+import { render } from '@testing-library/react';
+import mockFetch from 'jest-fetch-mock';
+import React from 'react';
+import CatalogApiPluginPage from './CatalogApiPluginPage';
+
+describe('CatalogApiPluginPage', () => {
+ it('should render', () => {
+ mockFetch.mockResponse(() => new Promise(() => {}));
+ const rendered = render(
+
+
+ ,
+ );
+ expect(rendered.getByText('Welcome to catalog-api!')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.tsx b/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.tsx
new file mode 100644
index 0000000000..dc47a74052
--- /dev/null
+++ b/plugins/api-docs/src/components/CatalogApiPluginPage/CatalogApiPluginPage.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 {
+ Content,
+ ContentHeader,
+ Header,
+ HeaderLabel,
+ InfoCard,
+ Page,
+ pageTheme,
+ SupportButton,
+} from '@backstage/core';
+import { Grid, Typography } from '@material-ui/core';
+import React, { FC } from 'react';
+
+const CatalogApiPluginPage: FC<{}> = () => (
+
+
+
+
+ A description of your plugin goes here.
+
+
+
+
+
+ All content should be wrapped in a card like this.
+
+
+
+
+ API Catalog
+
+
+
+
+);
+
+export default CatalogApiPluginPage;
diff --git a/plugins/api-docs/src/components/CatalogApiPluginPage/index.ts b/plugins/api-docs/src/components/CatalogApiPluginPage/index.ts
new file mode 100644
index 0000000000..7021d2bf9f
--- /dev/null
+++ b/plugins/api-docs/src/components/CatalogApiPluginPage/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 { default } from './CatalogApiPluginPage';
diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx
new file mode 100644
index 0000000000..c06c90e67f
--- /dev/null
+++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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, useEffect, useState } from 'react';
+import SwaggerUI from 'swagger-ui-react';
+import 'swagger-ui-react/swagger-ui.css';
+
+export const OpenApiDefinitionWidget: FC<{
+ definition: any;
+}> = ({ definition }) => {
+ // Due to a bug in the swagger-ui-react component, the component needs
+ // to be created without content first.
+ const [def, setDef] = useState('');
+
+ useEffect(() => {
+ const timer = setTimeout(() => setDef(definition), 0);
+ return () => clearTimeout(timer);
+ }, [definition, setDef]);
+
+ // TODO: This looks fine in the light theme, but wrong in dark mode. We need a custom stylesheet for swagger-ui.
+ // Till then, we add a white background to the swagger-ui to make it usable in dark mode.
+ // TODO: the typings do not allow a string while the component does (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46654)
+ return (
+
+
+
+ );
+};
diff --git a/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx b/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx
new file mode 100644
index 0000000000..4ceeed06e1
--- /dev/null
+++ b/plugins/api-docs/src/components/PlainApiDefinitionWidget/PlainApiDefinitionWidget.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 { CodeSnippet } from '@backstage/core';
+import React, { FC } from 'react';
+
+export const PlainApiDefinitionWidget: FC<{
+ definition: any;
+}> = ({ definition }) => {
+ return ;
+};
diff --git a/plugins/api-docs/src/index.ts b/plugins/api-docs/src/index.ts
new file mode 100644
index 0000000000..c82a2c263b
--- /dev/null
+++ b/plugins/api-docs/src/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 './components/ApiDefinitionCard/ApiDefinitionCard';
+export { plugin } from './plugin';
diff --git a/plugins/api-docs/src/plugin.test.ts b/plugins/api-docs/src/plugin.test.ts
new file mode 100644
index 0000000000..054e804382
--- /dev/null
+++ b/plugins/api-docs/src/plugin.test.ts
@@ -0,0 +1,23 @@
+/*
+ * 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 { plugin } from './plugin';
+
+describe('api-docs', () => {
+ it('should export plugin', () => {
+ expect(plugin).toBeDefined();
+ });
+});
diff --git a/plugins/api-docs/src/plugin.ts b/plugins/api-docs/src/plugin.ts
new file mode 100644
index 0000000000..90d8990aaa
--- /dev/null
+++ b/plugins/api-docs/src/plugin.ts
@@ -0,0 +1,30 @@
+/*
+ * 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 { createPlugin, createRouteRef } from '@backstage/core';
+import CatalogApiPluginPage from './components/CatalogApiPluginPage';
+
+export const rootRouteRef = createRouteRef({
+ path: '/api-docs',
+ title: 'api-docs',
+});
+
+export const plugin = createPlugin({
+ id: 'api-docs',
+ register({ router }) {
+ router.addRoute(rootRouteRef, CatalogApiPluginPage);
+ },
+});
diff --git a/plugins/api-docs/src/setupTests.ts b/plugins/api-docs/src/setupTests.ts
new file mode 100644
index 0000000000..8553642152
--- /dev/null
+++ b/plugins/api-docs/src/setupTests.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.
+ */
+
+import '@testing-library/jest-dom';
+
+require('jest-fetch-mock').enableMocks();