Skip to content

Commit

Permalink
feat(report-portal): Added global page for report portal plugin
Browse files Browse the repository at this point in the history
- added Instances Page
- added Projects Page
- added Launches Page
- Updated Report Portal API
- Added api pagination for table
- fixed tsc and lint issues in backend plugin
- added Readme files for both plugins

Signed-off-by: Yash Oswal <[email protected]>
  • Loading branch information
yashoswalyo committed Feb 28, 2024
1 parent fd3e9b8 commit 0f65bc4
Show file tree
Hide file tree
Showing 32 changed files with 1,884 additions and 166 deletions.
61 changes: 61 additions & 0 deletions plugins/report-portal-backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,64 @@ start` in the root directory, and then navigating to [/report-portal](http://loc
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory.

## Installation

- Install the plugin
```shell
yarn workspace backend add @janus-idp/backstage-plugin-report-portal-backend
```
- Update the following files

- Create `/packages/backend/src/plugins/report-portal.ts` and add following code:

```ts
import { createRouter } from '@appdev-platform/backstage-plugin-report-portal-backend';
import { Router } from 'express';

import { PluginEnvironment } from '../types';

export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({ logger: env.logger, config: env.config });
}
```

- Add following lines to `/packages/backend/src/index.ts`:

```ts
import reportPortal from './plugins/report-portal';
async function main() {
// add the files to create backend router
const reportPortalEnv = useHotMemoize(module, () =>
createEnv('report-portal'),
);
apiRouter.use('/report-portal', await reportPortal(reportPortalEnv));
}
```

- Add below configuration to `app-config.yaml`:

```yaml
reportPortal:
# under integrations you can configure-
# multiple instances of report portal
integrations:
# host address of your instance
# for e.g: report-portal.mycorp.com
- host: ${REPORT_PORTAL_HOST}
# Baser API url of your instance
# for e.g: https://report-portal.mycorp.com/api/
baseUrl: ${REPORT_PORTAL_BASE_URL}
# Get the API key from profile page of your instance
# for e.g: Bearer fae22be1-0000-0000-8392-de1635eed9f4
token: ${REPORT_PORTAL_TOKEN}
# (optional) Filter the projects by type
# Default: "INTERNAL"
filterType: 'INTERNAL'
```
1 change: 1 addition & 0 deletions plugins/report-portal-backend/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Config {
integrations: Array<{
/**
* Host of report portal url
* @visibility frontend
*/
host: string;
/**
Expand Down
2 changes: 1 addition & 1 deletion plugins/report-portal-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"tsc": "tsc"
},
"dependencies": {
"@backstage/backend-common": "^0.19.8",
"@backstage/backend-common": "^0.21.0",
"@backstage/backend-plugin-api": "^0.6.6",
"@backstage/backend-plugin-manager": "npm:@janus-idp/[email protected]",
"@backstage/config": "^1.1.1",
Expand Down
2 changes: 2 additions & 0 deletions plugins/report-portal-backend/src/service/router.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';

import express from 'express';
import request from 'supertest';
Expand All @@ -11,6 +12,7 @@ describe('createRouter', () => {
beforeAll(async () => {
const router = await createRouter({
logger: getVoidLogger(),
config: new ConfigReader({}),
});
app = express().use(router);
});
Expand Down
2 changes: 1 addition & 1 deletion plugins/report-portal-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface RouterOptions {
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { logger, config } = options;
const { config } = options;
const hostsConfig = config.getConfigArray('reportPortal.integrations');

const router = Router();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createServiceBuilder } from '@backstage/backend-common';
import { Config, ConfigReader } from '@backstage/config';
import { ConfigReader } from '@backstage/config';

import { Logger } from 'winston';

Expand Down
80 changes: 80 additions & 0 deletions plugins/report-portal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,83 @@ Your plugin has been added to the example app in this repository, meaning you'll
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.

## Prequisite

- [report-portal-backend](../report-portal/) plugin

## Installation:

- Run the following command in your backstage project

```shell
yarn workspace app add @janus-idp/report-portal
```

- Now import the components

- open `/packages/app/src/App.tsx` and add the following code

```js
import { ReportPortalGlobalPage } from '@janus-idp/backstage-plugin-report-portal';

export const AppBase = () => {
// In <FlatRoutes> add the following route
<Route path="/report-portal" element={<ReportPortalGlobalPage />} />;
};
```

- open `/packages/app/src/components/Root/Root.tsx` and add the following code

```js
import AssessmentIcon from '@material-ui/icons/Assessment';
//...
//...
export const Root = ({ children }: PropsWithChildren<{}>) => (
<SidebarPage>
<!-- Add the link to route in your sidebar component -->
<SidebarItem icon={AssessmentIcon} to="report-portal" text="Report Portal" />
</SidebarPage>
)
```

- To add a card on overview tab of entity page, open `/packages/app/src/components/catalog/EntityPage.tsx` and add the following code:

```js
import { ReportPortalOverviewCard } from '@janus-idp/backstage-plugin-report-portal';
const overviewContent = (
<Grid>
<!-- add your card on overview -->
<Grid item lg={4} md={6} xs={12}>
<ReportPortalOverviewCard variant="gridItem" />
</Grid>
</Grid>
);
```

- Add the below configuration to your `app-config.yaml` file

```yaml
reportPortal:
# under integrations you can configure-
# multiple instances of report portal
integrations:
# host address of your instance
# for e.g: report-portal.mycorp.com
- host: ${REPORT_PORTAL_HOST}
# Baser API url of your instance
# for e.g: https://report-portal.mycorp.com/api/
baseUrl: ${REPORT_PORTAL_BASE_URL}
# Get the API key from profile page of your instance
# for e.g: Bearer fae22be1-0000-0000-8392-de1635eed9f4
token: ${REPORT_PORTAL_TOKEN}
# (optional) Filter the projects by type
# Default: "INTERNAL"
filterType: 'INTERNAL'
```
20 changes: 19 additions & 1 deletion plugins/report-portal/config.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
export interface Config {}
export interface Config {
/**
* Configuration values for Report Portal plugin
*/
reportPortal: {
integrations: Array<{
/**
* Host of report portal url
* @visibility frontend
*/
host: string;
/**
* Type of projects to list
* @visibility frontend
*/
filterType: string;
}>;
};
}
1 change: 0 additions & 1 deletion plugins/report-portal/dev/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { createDevApp } from '@backstage/dev-utils';
import {
EntityAboutCard,
EntityHasSubcomponentsCard,
EntityLayout,
EntityLinksCard,
} from '@backstage/plugin-catalog';
import { EntityProvider } from '@backstage/plugin-catalog-react';
Expand Down
76 changes: 76 additions & 0 deletions plugins/report-portal/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"name": "@appdev-platform/backstage-plugin-report-portal",
"version": "0.1.0",
"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"
},
"backstage": {
"role": "frontend-plugin"
},
"sideEffects": false,
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"export-dynamic": "janus-cli package export-dynamic-plugin",
"lint": "backstage-cli package lint",
"postpack": "backstage-cli package postpack",
"postversion": "yarn run export-dynamic",
"prepack": "backstage-cli package prepack",
"start": "backstage-cli package start",
"test": "backstage-cli package test --passWithNoTests --coverage",
"tsc": "tsc"
},
"dependencies": {
"@backstage/catalog-model": "^1.4.4",
"@backstage/core-components": "^0.14.0",
"@backstage/core-plugin-api": "^1.9.0",
"@backstage/plugin-catalog-react": "^1.10.0",
"@backstage/theme": "^0.5.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.11.3",
"@material-ui/lab": "^4.0.0-alpha.61",
"moment": "^2.30.1",
"react-multi-progress": "^1.3.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "16.13.1 || ^17.0.0 || ^18.0.0",
"react-router-dom": "^6.22.0"
},
"devDependencies": {
"@backstage/cli": "0.25.2",
"@backstage/core-app-api": "1.11.0",
"@backstage/dev-utils": "1.0.22",
"@backstage/plugin-catalog": "^1.16.1",
"@backstage/test-utils": "1.5.0",
"@janus-idp/cli": "1.7.3",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"msw": "1.0.0"
},
"files": [
"dist",
"config.d.ts",
"dist-scalprum",
"app-config.janus-idp.yaml"
],
"configSchema": "config.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/janus-idp/backstage-plugins",
"directory": "plugins/report-portal"
},
"keywords": [
"backstage",
"plugin"
],
"homepage": "https://janus-idp.io/",
"bugs": "https://github.com/janus-idp/backstage-plugins/issues"
}
9 changes: 7 additions & 2 deletions plugins/report-portal/src/api/ReportPortalApi.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { ApiRef, createApiRef } from '@backstage/core-plugin-api';

import { LaunchDetailsResp, ProjectDetails } from './types';
import { LaunchDetailsResp, ProjectDetails, ProjectListResp } from './types';

/** @public */
export const reportPortalApiRef: ApiRef<ReportPortalApi> = createApiRef({
id: 'plugin.report-portal',
});

export type ReportPortalApi = {
getReportPortalBaseUrl: (host: string) => string;
getLaunchResults: (
projectId: string,
filter: string,
host: string,
filters: { [key: string]: string | number } | undefined,
) => Promise<LaunchDetailsResp>;
getProjectDetails: (
projectId: string,
host: string,
) => Promise<ProjectDetails>;
getInstanceDetails: (
host: string,
filters: { [key: string]: string | number } | undefined,
) => Promise<ProjectListResp>;
};
66 changes: 66 additions & 0 deletions plugins/report-portal/src/api/ReportPortalClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { DiscoveryApi } from '@backstage/core-plugin-api';

import { ReportPortalApi } from './ReportPortalApi';
import { LaunchDetailsResp, ProjectDetails, ProjectListResp } from './types';

export class ReportPortalClient implements ReportPortalApi {
constructor(private readonly discoveryApi: DiscoveryApi) {}

private async getBaseApiUrl() {
return `${await this.discoveryApi.getBaseUrl('report-portal')}/v1/`;
}

getReportPortalBaseUrl(host: string) {
return `https://${host}/`;
}

async getLaunchResults(
projectId: string,
host: string,
filters: { [key: string]: string | number } | undefined,
) {
const baseUrl = new URL(
`${projectId}/launch/latest`,
await this.getBaseApiUrl(),
);
if (filters) {
Object.keys(filters).forEach(key =>
baseUrl.searchParams.append(key, filters[key] as string),
);
}
baseUrl.searchParams.append('host', host);
const response = await fetch(baseUrl);
if (response.status >= 400 && response.status < 600) {
throw new Error('Failed to fetch launch details');
}
return (await response.json()) as LaunchDetailsResp;
}

async getProjectDetails(projectId: string, host: string) {
const baseUrl = new URL(`project/${projectId}`, await this.getBaseApiUrl());
baseUrl.searchParams.append('host', host);
const response = await fetch(baseUrl);
if (response.status >= 400 && response.status < 600) {
throw new Error('Failed to fetch project details');
}
return (await response.json()) as ProjectDetails;
}

async getInstanceDetails(
host: string,
filters: { [key: string]: string | number } | undefined,
) {
const baseUrl = new URL('project/list', await this.getBaseApiUrl());
if (filters) {
Object.keys(filters).forEach(key =>
baseUrl.searchParams.append(key, filters[key] as string),
);
}
baseUrl.searchParams.append('host', host);
const response = await fetch(baseUrl);
if (response.status >= 400 && response.status < 600) {
throw new Error('Failed to get instance details');
}
return (await response.json()) as ProjectListResp;
}
}
Loading

0 comments on commit 0f65bc4

Please sign in to comment.