feat(api-docs): add plugin that provides widgets that display api docs

This commit is contained in:
Dominik Henneke
2020-07-24 16:10:32 +02:00
parent f33c3dba21
commit 812659c024
15 changed files with 413 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
};
+9
View File
@@ -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]
+20
View File
@@ -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();
+53
View File
@@ -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"
]
}
@@ -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 (
<InfoCard title={title} subheader={type}>
<ApiDefinitionWidget type={type} definition={definition} />
</InfoCard>
);
};
@@ -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 <OpenApiDefinitionWidget definition={definition} />;
default:
return <PlainApiDefinitionWidget definition={definition} />;
}
};
@@ -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(
<ThemeProvider theme={lightTheme}>
<CatalogApiPluginPage />
</ThemeProvider>,
);
expect(rendered.getByText('Welcome to catalog-api!')).toBeInTheDocument();
});
});
@@ -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<{}> = () => (
<Page theme={pageTheme.tool}>
<Header title="Welcome to catalog-api!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Plugin title">
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">
All content should be wrapped in a card like this.
</Typography>
</InfoCard>
</Grid>
<Grid item>
<p>API Catalog</p>
</Grid>
</Grid>
</Content>
</Page>
);
export default CatalogApiPluginPage;
@@ -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';
@@ -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 (
<div style={{ backgroundColor: 'white' }}>
<SwaggerUI spec={(def as any) as object} />
</div>
);
};
@@ -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 <CodeSnippet text={definition} language="yaml" />;
};
+18
View File
@@ -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';
+23
View File
@@ -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();
});
});
+30
View File
@@ -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);
},
});
+19
View File
@@ -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();