Merge pull request #6923 from deepak-bhardwaj-ps/plugin-allure

#6528 Plugin allure
This commit is contained in:
Fredrik Adelöw
2021-08-31 19:21:10 +02:00
committed by GitHub
19 changed files with 553 additions and 0 deletions
+3
View File
@@ -133,6 +133,9 @@ kafka:
brokers:
- localhost:9092
allure:
baseUrl: http://localhost:5050/allure-docker-service
integrations:
github:
- host: github.com
+9
View File
@@ -0,0 +1,9 @@
---
title: Allure Reports
author: Deepak Bhardwaj
authorUrl: https://github.com/deepak-bhardwaj-ps
category: Reporting
description: View Allure reports for your components in Backstage.
documentation: https://github.com/backstage/backstage/blob/plugin-allure/plugins/allure/README.md
iconUrl: https://avatars.githubusercontent.com/u/5879127
npmPackageName: '@backstage/plugin-allure'
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint')],
};
+47
View File
@@ -0,0 +1,47 @@
# [Allure](https://docs.qameta.io/allure/)
Welcome to the Backstage Allure plugin. This plugin add an entity service page to display Allure test reports related to the service.
## Install
Run the below command from the `app` package directory.
```shell
yarn add @backstage/plugin-allure
```
alternatively, you can execute below command from the root directory of your Backstage app.
```shell
yarn workspace app add @backstage/plugin-allure
```
## Configure
### Configure Allure service
Add below configuration in the `app-config.yaml`.
```yaml
allure:
baseUrl: <ALLURE_SERVICE_BASE_URL> # Example: https://allure.my-company.net or when running allure locally, http://localhost:5050/allure-docker-service
```
### Setup entity service page
Add `EntityAllureReportContent` in the `EntityPage.tsx` like below:
```diff
+ import { EntityAllureReportContent } from '@backstage/plugin-allure';
...
const serviceEntityPage = (
<EntityLayoutWrapper>
...
+ <EntityLayout.Route path="/allure" title="Allure Report">
+ <EntityAllureReportContent />
+ </EntityLayout.Route>
</EntityLayoutWrapper>
);
```
+27
View File
@@ -0,0 +1,27 @@
## API Report File for "@backstage/plugin-allure"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="react" />
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
// Warning: (ae-missing-release-tag) "allurePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const allurePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{}
>;
// Warning: (ae-missing-release-tag) "EntityAllureReportContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityAllureReportContent: () => JSX.Element;
// (No @packageDocumentation comment for this package)
```
+11
View File
@@ -0,0 +1,11 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: allure-example-service
description: Allure Example Service
annotations:
qameta.io/allure-project: sample
spec:
type: service
lifecycle: experimental
owner: team-a
+55
View File
@@ -0,0 +1,55 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { createDevApp } from '@backstage/dev-utils';
import { EntityAllureReportContent } from '../src/plugin';
import { Content, Header, Page } from '@backstage/core-components';
import { EntityProvider } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import exampleEntity from './example-entity.yaml';
import { allureApiRef } from '../src/api';
createDevApp()
.registerApi({
api: allureApiRef,
deps: {},
factory: () =>
({
async getReportUrl(projectId: string) {
return Promise.resolve(
// Follow the instructions from https://github.com/fescobar/allure-docker-service-ui
// to setup Allure service locally
`http://localhost:5050/allure-docker-service/projects/${projectId}/reports/latest/index.html`,
);
},
} as unknown as typeof allureApiRef.T),
})
.addPage({
element: (
<Page themeId="home">
<Header title="Allure Report" />
<Content>
<EntityProvider entity={exampleEntity as any as Entity}>
<EntityAllureReportContent />
</EntityProvider>
</Content>
</Page>
),
title: 'Allure Report',
path: '/allure',
})
.render();
+69
View File
@@ -0,0 +1,69 @@
{
"name": "@backstage/plugin-allure",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
"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.9.0",
"@backstage/core-components": "^0.3.3",
"@backstage/core-plugin-api": "^0.1.6",
"@backstage/plugin-catalog-react": "^0.4.3",
"@backstage/theme": "^0.2.10",
"@material-ui/core": "^4.12.2",
"@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": "^17.2.4"
},
"devDependencies": {
"@backstage/cli": "^0.7.9",
"@backstage/core-app-api": "^0.1.10",
"@backstage/dev-utils": "^0.2.7",
"@backstage/test-utils": "^0.1.17",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.1.8",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32",
"cross-fetch": "^3.0.6",
"msw": "^0.29.0"
},
"files": [
"dist"
],
"configSchema": {
"$schema": "https://backstage.io/schema/config-v1",
"title": "@backstage/allure",
"type": "object",
"properties": {
"allure": {
"type": "object",
"properties": {
"baseUrl": {
"type": "string",
"visibility": "frontend"
}
}
}
}
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { createApiRef } from '@backstage/core-plugin-api';
export type AllureApi = {
getReportUrl(projectId: string): Promise<string>;
};
export const allureApiRef = createApiRef<AllureApi>({
id: 'allure-api',
});
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { ConfigApi } from '@backstage/core-plugin-api';
import { AllureApi } from './AllureApi';
export class AllureApiClient implements AllureApi {
readonly configApi: ConfigApi;
constructor(options: { configApi: ConfigApi }) {
this.configApi = options.configApi;
}
async getReportUrl(projectId: string): Promise<string> {
const baseUrl = this.configApi.getString('allure.baseUrl');
return `${baseUrl}/projects/${projectId}/reports/latest/index.html`;
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { allureApiRef } from './AllureApi';
export { AllureApiClient } from './AllureApiClient';
@@ -0,0 +1,44 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { AllureReportComponent } from './AllureReportComponent';
import { ThemeProvider } from '@material-ui/core';
import { lightTheme } from '@backstage/theme';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { msw, renderInTestApp } from '@backstage/test-utils';
describe('ExampleComponent', () => {
const server = setupServer();
// Enable sane handlers for network requests
msw.setupDefaultHandlers(server);
// setup mock response
beforeEach(() => {
server.use(
rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
);
});
it('should render', async () => {
const rendered = await renderInTestApp(
<ThemeProvider theme={lightTheme}>
<AllureReportComponent />
</ThemeProvider>,
);
expect(rendered.getByText('Missing Annotation')).toBeInTheDocument();
});
});
@@ -0,0 +1,65 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { useApi } from '@backstage/core-plugin-api';
import { allureApiRef } from '../../api';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
ALLURE_PROJECT_ID_ANNOTATION,
isAllureReportAvailable,
getAllureProjectId,
} from '../annotationHelpers';
import {
MissingAnnotationEmptyState,
Progress,
} from '@backstage/core-components';
import { useAsync } from 'react-use';
import { Entity } from '@backstage/catalog-model';
const AllureReport = (props: { entity: Entity }) => {
const allureApi = useApi(allureApiRef);
const allureProjectId = getAllureProjectId(props.entity);
const { value, loading } = useAsync(async () => {
const url = await allureApi.getReportUrl(allureProjectId);
return url;
});
if (loading) {
return <Progress />;
}
return (
<iframe
style={{
display: 'table',
width: '100%',
height: '100%',
}}
title="Allure Report"
src={value}
/>
);
};
export const AllureReportComponent = () => {
const { entity } = useEntity();
const isReportAvailable = entity && isAllureReportAvailable(entity);
if (isReportAvailable) return <AllureReport entity={entity} />;
return (
<MissingAnnotationEmptyState annotation={ALLURE_PROJECT_ID_ANNOTATION} />
);
};
@@ -0,0 +1,16 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { AllureReportComponent } from './AllureReportComponent';
@@ -0,0 +1,25 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { Entity } from '@backstage/catalog-model';
export const ALLURE_PROJECT_ID_ANNOTATION = 'qameta.io/allure-project';
export const isAllureReportAvailable = (entity: Entity) =>
Boolean(entity.metadata.annotations?.[ALLURE_PROJECT_ID_ANNOTATION]);
export const getAllureProjectId = (entity: Entity) => {
return entity?.metadata.annotations?.[ALLURE_PROJECT_ID_ANNOTATION] ?? '';
};
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { allurePlugin, EntityAllureReportContent } from './plugin';
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { allurePlugin } from './plugin';
describe('allure', () => {
it('should export plugin', () => {
expect(allurePlugin).toBeDefined();
});
});
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 {
configApiRef,
createApiFactory,
createPlugin,
createRoutableExtension,
createRouteRef,
} from '@backstage/core-plugin-api';
import { AllureApiClient, allureApiRef } from './api';
export const allureRouteRef = createRouteRef({
title: 'allure-report',
});
export const allurePlugin = createPlugin({
id: 'allure',
apis: [
createApiFactory({
api: allureApiRef,
deps: {
configApi: configApiRef,
},
factory: ({ configApi }) => new AllureApiClient({ configApi }),
}),
],
routes: {
root: allureRouteRef,
},
});
export const EntityAllureReportContent = allurePlugin.provide(
createRoutableExtension({
component: () =>
import('./components/AllureReportComponent').then(
m => m.AllureReportComponent,
),
mountPoint: allureRouteRef,
}),
);
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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';
import 'cross-fetch/polyfill';