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();