From b1887b3fefe56fc4b7ac1f5c925d0b1822b8dad1 Mon Sep 17 00:00:00 2001 From: Yash Oswal Date: Fri, 16 Feb 2024 15:15:36 +0530 Subject: [PATCH] feat(report-portal): added report portal frontend and backend plugins Signed-off-by: Yash Oswal --- plugins/report-portal-backend/.eslintrc.js | 1 + plugins/report-portal-backend/README.md | 14 ++ .../app-config.janus-idp.yaml | 5 + plugins/report-portal-backend/config.d.ts | 22 ++ plugins/report-portal-backend/package.json | 82 +++++++ plugins/report-portal-backend/src/alpha.ts | 1 + .../src/dynamic/index.ts | 11 + plugins/report-portal-backend/src/index.ts | 8 + plugins/report-portal-backend/src/plugin.ts | 33 +++ plugins/report-portal-backend/src/run.ts | 19 ++ .../src/service/router.test.ts | 30 +++ .../src/service/router.ts | 56 +++++ .../src/service/standaloneServer.ts | 40 +++ .../report-portal-backend/src/setupTests.ts | 1 + plugins/report-portal-backend/tsconfig.json | 9 + plugins/report-portal-backend/turbo.json | 9 + plugins/report-portal/.eslintrc.js | 1 + plugins/report-portal/README.md | 13 + .../report-portal/app-config.janus-idp.yaml | 8 + plugins/report-portal/config.d.ts | 1 + plugins/report-portal/dev/index.tsx | 43 ++++ .../report-portal/src/api/ReportPortalApi.ts | 20 ++ plugins/report-portal/src/api/index.ts | 3 + plugins/report-portal/src/api/types.ts | 68 ++++++ .../ReportPortalOverviewCard.tsx | 229 ++++++++++++++++++ .../ReportPortalOverviewCard/index.ts | 1 + plugins/report-portal/src/hooks/index.ts | 2 + .../src/hooks/useLaunchDetails.ts | 27 +++ .../src/hooks/useProjectDetails.ts | 24 ++ plugins/report-portal/src/mocks/entity.ts | 22 ++ plugins/report-portal/src/mocks/index.ts | 1 + plugins/report-portal/src/plugin.test.ts | 7 + plugins/report-portal/src/setupTests.ts | 1 + plugins/report-portal/tsconfig.json | 9 + plugins/report-portal/turbo.json | 9 + yarn.lock | 121 ++++++++- 36 files changed, 941 insertions(+), 10 deletions(-) create mode 100644 plugins/report-portal-backend/.eslintrc.js create mode 100644 plugins/report-portal-backend/README.md create mode 100644 plugins/report-portal-backend/app-config.janus-idp.yaml create mode 100644 plugins/report-portal-backend/config.d.ts create mode 100644 plugins/report-portal-backend/package.json create mode 100644 plugins/report-portal-backend/src/alpha.ts create mode 100644 plugins/report-portal-backend/src/dynamic/index.ts create mode 100644 plugins/report-portal-backend/src/index.ts create mode 100644 plugins/report-portal-backend/src/plugin.ts create mode 100644 plugins/report-portal-backend/src/run.ts create mode 100644 plugins/report-portal-backend/src/service/router.test.ts create mode 100644 plugins/report-portal-backend/src/service/router.ts create mode 100644 plugins/report-portal-backend/src/service/standaloneServer.ts create mode 100644 plugins/report-portal-backend/src/setupTests.ts create mode 100644 plugins/report-portal-backend/tsconfig.json create mode 100644 plugins/report-portal-backend/turbo.json create mode 100644 plugins/report-portal/.eslintrc.js create mode 100644 plugins/report-portal/README.md create mode 100644 plugins/report-portal/app-config.janus-idp.yaml create mode 100644 plugins/report-portal/config.d.ts create mode 100644 plugins/report-portal/dev/index.tsx create mode 100644 plugins/report-portal/src/api/ReportPortalApi.ts create mode 100644 plugins/report-portal/src/api/index.ts create mode 100644 plugins/report-portal/src/api/types.ts create mode 100644 plugins/report-portal/src/components/ReportPortalOverviewCard/ReportPortalOverviewCard.tsx create mode 100644 plugins/report-portal/src/components/ReportPortalOverviewCard/index.ts create mode 100644 plugins/report-portal/src/hooks/index.ts create mode 100644 plugins/report-portal/src/hooks/useLaunchDetails.ts create mode 100644 plugins/report-portal/src/hooks/useProjectDetails.ts create mode 100644 plugins/report-portal/src/mocks/entity.ts create mode 100644 plugins/report-portal/src/mocks/index.ts create mode 100644 plugins/report-portal/src/plugin.test.ts create mode 100644 plugins/report-portal/src/setupTests.ts create mode 100644 plugins/report-portal/tsconfig.json create mode 100644 plugins/report-portal/turbo.json diff --git a/plugins/report-portal-backend/.eslintrc.js b/plugins/report-portal-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/report-portal-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/report-portal-backend/README.md b/plugins/report-portal-backend/README.md new file mode 100644 index 0000000000..5fc78f75aa --- /dev/null +++ b/plugins/report-portal-backend/README.md @@ -0,0 +1,14 @@ +# report-portal + +Welcome to the report-portal backend plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn +start` in the root directory, and then navigating to [/report-portal](http://localhost:3000/report-portal). + +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. diff --git a/plugins/report-portal-backend/app-config.janus-idp.yaml b/plugins/report-portal-backend/app-config.janus-idp.yaml new file mode 100644 index 0000000000..b853fcd414 --- /dev/null +++ b/plugins/report-portal-backend/app-config.janus-idp.yaml @@ -0,0 +1,5 @@ +reportPortal: + integrations: + - host: ${REPORT_PORTAL_HOST} + baseUrl: ${REPORT_PORTAL_BASE_URL} + token: ${REPORT_PORTAL_TOKEN} diff --git a/plugins/report-portal-backend/config.d.ts b/plugins/report-portal-backend/config.d.ts new file mode 100644 index 0000000000..3dec6e4add --- /dev/null +++ b/plugins/report-portal-backend/config.d.ts @@ -0,0 +1,22 @@ +export interface Config { + /** + * Configuration values for Report Portal plugin + */ + reportPortal: { + integrations: Array<{ + /** + * Host of report portal url + */ + host: string; + /** + * Base api url for report portal instance + */ + baseUrl: string; + /** + * The Api token that will be used to + * @visibility secret + */ + token: string; + }>; + }; +} diff --git a/plugins/report-portal-backend/package.json b/plugins/report-portal-backend/package.json new file mode 100644 index 0000000000..034f16aea7 --- /dev/null +++ b/plugins/report-portal-backend/package.json @@ -0,0 +1,82 @@ +{ + "name": "@appdev-platform/backstage-plugin-report-portal-backend", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "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/backend-common": "^0.19.8", + "@backstage/backend-plugin-api": "^0.6.6", + "@backstage/backend-plugin-manager": "npm:@janus-idp/backend-plugin-manager@0.0.2-janus.5", + "@backstage/config": "^1.1.1", + "@types/express": "^*", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "http-proxy-middleware": "^2.0.6", + "node-fetch": "^2.6.7", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "0.23.0", + "@janus-idp/cli": "1.7.1", + "@types/supertest": "2.0.12", + "msw": "1.0.0", + "supertest": "6.2.4" + }, + "files": [ + "dist", + "config.d.ts", + "dist-dynamic/*.*", + "dist-dynamic/dist/**", + "dist-dynamic/alpha/*", + "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" +} diff --git a/plugins/report-portal-backend/src/alpha.ts b/plugins/report-portal-backend/src/alpha.ts new file mode 100644 index 0000000000..1283089976 --- /dev/null +++ b/plugins/report-portal-backend/src/alpha.ts @@ -0,0 +1 @@ +export { reportPortalPlugin } from './plugin'; diff --git a/plugins/report-portal-backend/src/dynamic/index.ts b/plugins/report-portal-backend/src/dynamic/index.ts new file mode 100644 index 0000000000..8bd3fca15a --- /dev/null +++ b/plugins/report-portal-backend/src/dynamic/index.ts @@ -0,0 +1,11 @@ +import { BackendDynamicPluginInstaller } from '@backstage/backend-plugin-manager'; + +import { createRouter } from '../service/router'; + +export const dynamicPluginInstaller: BackendDynamicPluginInstaller = { + kind: 'legacy', + router: { + pluginID: 'report-portal', + createPlugin: createRouter, + }, +}; diff --git a/plugins/report-portal-backend/src/index.ts b/plugins/report-portal-backend/src/index.ts new file mode 100644 index 0000000000..8e2a7199c3 --- /dev/null +++ b/plugins/report-portal-backend/src/index.ts @@ -0,0 +1,8 @@ +/** + * The report-portal backend plugin. + * + * @packageDocumentation + */ + +export * from './dynamic/index'; +export * from './service/router'; diff --git a/plugins/report-portal-backend/src/plugin.ts b/plugins/report-portal-backend/src/plugin.ts new file mode 100644 index 0000000000..14e28240a4 --- /dev/null +++ b/plugins/report-portal-backend/src/plugin.ts @@ -0,0 +1,33 @@ +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; + +import { createRouter } from './service/router'; + +/** + * The report-portal backend plugin. + * + * @alpha + */ +export const reportPortalPlugin = createBackendPlugin({ + pluginId: 'report-portal', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + http: coreServices.httpRouter, + }, + async init({ config, logger, http }) { + http.use(() => + createRouter({ + config: config, + logger: loggerToWinstonLogger(logger), + }), + ); + }, + }); + }, +}); diff --git a/plugins/report-portal-backend/src/run.ts b/plugins/report-portal-backend/src/run.ts new file mode 100644 index 0000000000..e48fa22f3f --- /dev/null +++ b/plugins/report-portal-backend/src/run.ts @@ -0,0 +1,19 @@ +import { getRootLogger } from '@backstage/backend-common'; + +import yn from 'yn'; + +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error('Standalone server failed:', err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/report-portal-backend/src/service/router.test.ts b/plugins/report-portal-backend/src/service/router.test.ts new file mode 100644 index 0000000000..0e13957bc8 --- /dev/null +++ b/plugins/report-portal-backend/src/service/router.test.ts @@ -0,0 +1,30 @@ +import { getVoidLogger } from '@backstage/backend-common'; + +import express from 'express'; +import request from 'supertest'; + +import { createRouter } from './router'; + +describe('createRouter', () => { + let app: express.Express; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); diff --git a/plugins/report-portal-backend/src/service/router.ts b/plugins/report-portal-backend/src/service/router.ts new file mode 100644 index 0000000000..ad5fe27910 --- /dev/null +++ b/plugins/report-portal-backend/src/service/router.ts @@ -0,0 +1,56 @@ +import { errorHandler } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; + +import express from 'express'; +import Router from 'express-promise-router'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +import { Logger } from 'winston'; + +export interface RouterOptions { + logger: Logger; + config: Config; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, config } = options; + const hostsConfig = config.getConfigArray('reportPortal.integrations'); + + const router = Router(); + router.use(express.json()); + + router.get('/*', (req, res, next) => { + const hostName = req.query.host; + if (!hostName) { + res.status(500).json({ message: 'Oops, I think you forgot something?' }); + return; + } + const reqConfig = hostsConfig + .find(instance => instance.getString('host') === hostName) + ?.get() as PluginConfig; + + const proxy = createProxyMiddleware({ + target: reqConfig.baseUrl, + changeOrigin: true, + secure: false, + headers: { + Authorization: reqConfig.token, + }, + pathRewrite: { + ['/api/report-portal']: '', + }, + }); + + proxy(req, res, next); + }); + + router.use(errorHandler()); + return router; +} + +type PluginConfig = { + host: string; + baseUrl: string; + token: string; +}; diff --git a/plugins/report-portal-backend/src/service/standaloneServer.ts b/plugins/report-portal-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..9964ca65d2 --- /dev/null +++ b/plugins/report-portal-backend/src/service/standaloneServer.ts @@ -0,0 +1,40 @@ +import { createServiceBuilder } from '@backstage/backend-common'; +import { Config, ConfigReader } from '@backstage/config'; + +import { Logger } from 'winston'; + +import { Server } from 'http'; + +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const config = new ConfigReader({}); + const logger = options.logger.child({ service: 'report-portal-backend' }); + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + config, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/report-portal', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error('Dev server failed:', err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/report-portal-backend/src/setupTests.ts b/plugins/report-portal-backend/src/setupTests.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/plugins/report-portal-backend/src/setupTests.ts @@ -0,0 +1 @@ +export {}; diff --git a/plugins/report-portal-backend/tsconfig.json b/plugins/report-portal-backend/tsconfig.json new file mode 100644 index 0000000000..7d95752d32 --- /dev/null +++ b/plugins/report-portal-backend/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@backstage/cli/config/tsconfig.json", + "include": ["src", "dev", "migrations"], + "exclude": ["node_modules"], + "compilerOptions": { + "outDir": "../../dist-types/plugins/report-portal", + "rootDir": "." + } +} diff --git a/plugins/report-portal-backend/turbo.json b/plugins/report-portal-backend/turbo.json new file mode 100644 index 0000000000..22f01e23b1 --- /dev/null +++ b/plugins/report-portal-backend/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "pipeline": { + "tsc": { + "outputs": ["../../dist-types/plugins/report-portal/**"], + "dependsOn": ["^tsc"] + } + } +} diff --git a/plugins/report-portal/.eslintrc.js b/plugins/report-portal/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/report-portal/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/report-portal/README.md b/plugins/report-portal/README.md new file mode 100644 index 0000000000..cf4433cb3d --- /dev/null +++ b/plugins/report-portal/README.md @@ -0,0 +1,13 @@ +# report-portal + +Welcome to the report-portal plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/report-portal](http://localhost:3000/report-portal). + +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. diff --git a/plugins/report-portal/app-config.janus-idp.yaml b/plugins/report-portal/app-config.janus-idp.yaml new file mode 100644 index 0000000000..77db7255e1 --- /dev/null +++ b/plugins/report-portal/app-config.janus-idp.yaml @@ -0,0 +1,8 @@ +dynamicPlugins: + frontend: + janus-idp.backstage-plugin-report-portal: + apiFactories: [] + appIcons: [] + dynamicRoutes: [] + mountPoints: [] + routeBindings: [] diff --git a/plugins/report-portal/config.d.ts b/plugins/report-portal/config.d.ts new file mode 100644 index 0000000000..5728ccd9e1 --- /dev/null +++ b/plugins/report-portal/config.d.ts @@ -0,0 +1 @@ +export interface Config {} diff --git a/plugins/report-portal/dev/index.tsx b/plugins/report-portal/dev/index.tsx new file mode 100644 index 0000000000..623d94d88a --- /dev/null +++ b/plugins/report-portal/dev/index.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import { createDevApp } from '@backstage/dev-utils'; +import { + EntityAboutCard, + EntityHasSubcomponentsCard, + EntityLayout, + EntityLinksCard, +} from '@backstage/plugin-catalog'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; + +import { Grid } from '@material-ui/core'; + +import { mockEntity } from '../src/mocks'; +import { ReportPortalOverviewCard, reportPortalPlugin } from '../src/plugin'; + +const overviewContent = ( + + + + + + + + + + + + + + +); + +createDevApp() + .registerPlugin(reportPortalPlugin) + .addPage({ + element: ( + {overviewContent} + ), + title: 'Entity Page', + path: '/catalog/default/component/example-for-report-portal', + }) + .render(); diff --git a/plugins/report-portal/src/api/ReportPortalApi.ts b/plugins/report-portal/src/api/ReportPortalApi.ts new file mode 100644 index 0000000000..4c9984cef7 --- /dev/null +++ b/plugins/report-portal/src/api/ReportPortalApi.ts @@ -0,0 +1,20 @@ +import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; + +import { LaunchDetailsResp, ProjectDetails } from './types'; + +/** @public */ +export const reportPortalApiRef: ApiRef = createApiRef({ + id: 'plugin.report-portal', +}); + +export type ReportPortalApi = { + getLaunchResults: ( + projectId: string, + filter: string, + host: string, + ) => Promise; + getProjectDetails: ( + projectId: string, + host: string, + ) => Promise; +}; diff --git a/plugins/report-portal/src/api/index.ts b/plugins/report-portal/src/api/index.ts new file mode 100644 index 0000000000..d990bab3b6 --- /dev/null +++ b/plugins/report-portal/src/api/index.ts @@ -0,0 +1,3 @@ +export * from './ReportPortalClient'; +export * from './ReportPortalApi'; +export * from './types'; diff --git a/plugins/report-portal/src/api/types.ts b/plugins/report-portal/src/api/types.ts new file mode 100644 index 0000000000..8f1b5fc40c --- /dev/null +++ b/plugins/report-portal/src/api/types.ts @@ -0,0 +1,68 @@ +export type ProjectDetails = { + projectId: number; + projectName: string; + configuration: { + subTypes: { + [key: string]: [ + { + id: number; + locator: string; + typeRef: string; + longName: string; + shortName: string; + color: string; + }, + ]; + }; + }; + users: [ + { + login: string; + projectRole: string; + }, + ]; + creationDate: number; +}; + +export type LaunchDetails = { + owner: string; + share: boolean; + description: string; + id: number; + uuid: string; + name: string; + number: number; + startTime: number; + endTime: number; + lastModified: number; + status: string; + statistics: { + executions: { + passed: number; + failed: number; + skipped: number; + total: number; + }; + defects: { + [key: string]: { + total: number; + [key: string]: number; + }; + }; + mode: string; + analysing: []; + approximateDuration: number; + hasRetries: boolean; + rerun: boolean; + }; +}; + +export type LaunchDetailsResp = { + content: [LaunchDetails]; + page: { + number: number; + size: number; + totalElements: number; + totalPages: number; + }; +}; diff --git a/plugins/report-portal/src/components/ReportPortalOverviewCard/ReportPortalOverviewCard.tsx b/plugins/report-portal/src/components/ReportPortalOverviewCard/ReportPortalOverviewCard.tsx new file mode 100644 index 0000000000..5d5270770b --- /dev/null +++ b/plugins/report-portal/src/components/ReportPortalOverviewCard/ReportPortalOverviewCard.tsx @@ -0,0 +1,229 @@ +import React, { useEffect, useState } from 'react'; +import MultiProgress from 'react-multi-progress'; + +import { InfoCard, InfoCardVariants } from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; + +import { + Divider, + Grid, + List, + ListItem, + ListItemSecondaryAction, + ListItemText, + makeStyles, + Theme, + Typography, +} from '@material-ui/core'; +import { Skeleton } from '@material-ui/lab'; + +import { useLaunchDetails, useProjectDetails } from '../../hooks'; + +const HeaderComponent = (props: { total: number }) => { + return ( + + Test Statistics + {props.total === -1 ? ( + + + + ) : ( + Total: {props.total} + )} + + ); +}; + +type Defect = { id: number; name: string; total: number; color: string }; + +const useStylesForDefect = makeStyles((theme: Theme) => ({ + status: { + '&::before': { + width: '0.7em', + height: '0.7em', + display: 'inline-block', + marginRight: theme.spacing(1), + borderRadius: '50%', + content: '""', + backgroundColor: (props: { backgroundColor: string }) => + props.backgroundColor, + }, + }, +})); + +const DefectStatus = (props: { color: string; children: string }) => { + const classes = useStylesForDefect({ backgroundColor: props.color }); + return ( + + ); +}; + +const useStyles = makeStyles({ + results: { + fontWeight: 'bold', + }, +}); + +export const ReportPortalOverviewCard = (props: { + variant: InfoCardVariants; +}) => { + const { entity } = useEntity(); + const classes = useStyles(); + const projectId = + entity.metadata.annotations?.['reportportal.io/project-name'] ?? ''; + const launchName = + entity.metadata.annotations?.['reportportal.io/launch-name'] ?? ''; + const hostName = entity.metadata.annotations?.['reportportal.io/host'] ?? ''; + const [defects, setDefects] = useState([]); + + const { loading, launchDetails } = useLaunchDetails( + projectId, + launchName, + hostName, + ); + const { loading: projectLoading, projectDetails } = useProjectDetails( + projectId, + hostName, + ); + + useEffect(() => { + if (!loading && launchDetails && projectDetails) { + const tempArr: Defect[] = []; + Object.keys(launchDetails.statistics.defects).forEach(defect => { + tempArr.push({ + name: projectDetails.configuration.subTypes?.[defect.toUpperCase()][0] + .longName, + total: launchDetails.statistics.defects?.[defect].total, + color: + projectDetails.configuration.subTypes?.[defect.toUpperCase()][0] + .color, + id: projectDetails.configuration.subTypes?.[defect.toUpperCase()][0] + .id, + }); + }); + setDefects(tempArr); + } + }, [loading, launchDetails, projectDetails]); + + return ( + + } + variant={props.variant} + divider + deepLink={{ + link: `https://${hostName}/ui/#${projectId}/launches/latest/${launchDetails?.id}`, + title: 'View on Report Portal', + }} + > + + {loading ? ( + <> + + + + + + + + + + + + + + ) : ( + <> + + + + + + Passed: {launchDetails!.statistics.executions.passed ?? 0} + + + + + Failed: {launchDetails!.statistics.executions.failed ?? 0} + + + + + Skipped: {launchDetails!.statistics.executions.skipped ?? 0} + + + + )} + + + + + {!projectLoading ? ( + + {defects.length > 0 ? ( + defects.map(defect => ( + + + {defect.name} + + } + /> + + {defect.total} + + + )) + ) : ( + + No defects found + + )} + + ) : ( + + )} + + + + ); +}; diff --git a/plugins/report-portal/src/components/ReportPortalOverviewCard/index.ts b/plugins/report-portal/src/components/ReportPortalOverviewCard/index.ts new file mode 100644 index 0000000000..e2c35565e6 --- /dev/null +++ b/plugins/report-portal/src/components/ReportPortalOverviewCard/index.ts @@ -0,0 +1 @@ +export { ReportPortalOverviewCard } from './ReportPortalOverviewCard'; diff --git a/plugins/report-portal/src/hooks/index.ts b/plugins/report-portal/src/hooks/index.ts new file mode 100644 index 0000000000..6bae4c991c --- /dev/null +++ b/plugins/report-portal/src/hooks/index.ts @@ -0,0 +1,2 @@ +export * from './useProjectDetails'; +export * from './useLaunchDetails'; diff --git a/plugins/report-portal/src/hooks/useLaunchDetails.ts b/plugins/report-portal/src/hooks/useLaunchDetails.ts new file mode 100644 index 0000000000..9503616078 --- /dev/null +++ b/plugins/report-portal/src/hooks/useLaunchDetails.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react'; + +import { useApi } from '@backstage/core-plugin-api'; + +import { LaunchDetails, reportPortalApiRef } from '../api'; + +export function useLaunchDetails( + projectId: string, + launchName: string, + hostName: string, +) { + const reportPortalApi = useApi(reportPortalApiRef); + const [loading, setLoading] = useState(true); + const [launchDetails, setLaunchDetails] = useState(); + + useEffect(() => { + setLoading(true); + reportPortalApi + .getLaunchResults(projectId, launchName, hostName) + .then(res => { + setLaunchDetails(res.content[0]); + setLoading(false); + }); + }, [projectId, launchName, hostName, reportPortalApi]); + + return { loading, launchDetails }; +} diff --git a/plugins/report-portal/src/hooks/useProjectDetails.ts b/plugins/report-portal/src/hooks/useProjectDetails.ts new file mode 100644 index 0000000000..2dec5ce0dc --- /dev/null +++ b/plugins/report-portal/src/hooks/useProjectDetails.ts @@ -0,0 +1,24 @@ +import React from 'react'; + +import { useApi } from '@backstage/core-plugin-api'; + +import { ProjectDetails, reportPortalApiRef } from '../api'; + +export function useProjectDetails( + projectId: string, + host: string, +): { loading: boolean; projectDetails: ProjectDetails | undefined } { + const reportPortalApi = useApi(reportPortalApiRef); + const [projectDetails, setProjectDetails] = React.useState(); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + setLoading(true); + reportPortalApi.getProjectDetails(projectId, host).then(resp => { + setProjectDetails(resp); + setLoading(false); + }); + }, [projectId, reportPortalApi, host]); + + return { loading, projectDetails }; +} diff --git a/plugins/report-portal/src/mocks/entity.ts b/plugins/report-portal/src/mocks/entity.ts new file mode 100644 index 0000000000..cb16d1aa44 --- /dev/null +++ b/plugins/report-portal/src/mocks/entity.ts @@ -0,0 +1,22 @@ +import { Entity } from '@backstage/catalog-model'; + +export const mockEntity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'example-for-report-portal', + title: 'Example App', + namespace: 'default', + annotations: { + 'reportportal.io/host': + 'reportportal-hydra.apps.ocp-c1.prod.psi.redhat.com', + 'reportportal.io/project-name': 'dxp_qe', + 'reportportal.io/launch-name': 'Demo API Tests', + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }, +}; diff --git a/plugins/report-portal/src/mocks/index.ts b/plugins/report-portal/src/mocks/index.ts new file mode 100644 index 0000000000..0228249964 --- /dev/null +++ b/plugins/report-portal/src/mocks/index.ts @@ -0,0 +1 @@ +export { mockEntity } from './entity'; diff --git a/plugins/report-portal/src/plugin.test.ts b/plugins/report-portal/src/plugin.test.ts new file mode 100644 index 0000000000..0c53dc48cb --- /dev/null +++ b/plugins/report-portal/src/plugin.test.ts @@ -0,0 +1,7 @@ +import { reportPortalPlugin } from './plugin'; + +describe('report-portal', () => { + it('should export plugin', () => { + expect(reportPortalPlugin).toBeDefined(); + }); +}); diff --git a/plugins/report-portal/src/setupTests.ts b/plugins/report-portal/src/setupTests.ts new file mode 100644 index 0000000000..7b0828bfa8 --- /dev/null +++ b/plugins/report-portal/src/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/plugins/report-portal/tsconfig.json b/plugins/report-portal/tsconfig.json new file mode 100644 index 0000000000..25b14614f6 --- /dev/null +++ b/plugins/report-portal/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@backstage/cli/config/tsconfig.json", + "include": ["src", "dev"], + "exclude": ["node_modules"], + "compilerOptions": { + "outDir": "../../dist-types/plugins/report-portal", + "rootDir": "." + } +} diff --git a/plugins/report-portal/turbo.json b/plugins/report-portal/turbo.json new file mode 100644 index 0000000000..22f01e23b1 --- /dev/null +++ b/plugins/report-portal/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "pipeline": { + "tsc": { + "outputs": ["../../dist-types/plugins/report-portal/**"], + "dependsOn": ["^tsc"] + } + } +} diff --git a/yarn.lock b/yarn.lock index ce46597578..f9ee164c1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2634,7 +2634,6 @@ version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== - dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-template-literals@^7.22.5": @@ -13420,6 +13419,20 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/jest-dom@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.0.0.tgz#d2ba5a3fd13724d5966b3f8cd24d2cedcab4fa76" + integrity sha512-Ye2R3+/oM27jir8CzYPmuWdavTaKwNZcu0d22L9pO/vnOYE0wmrtpw79TQJa8H6gV8/i7yd+pLaqeLlA0rTMfg== + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + "@testing-library/react-hooks@8.0.1": version "8.0.1" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" @@ -13442,6 +13455,20 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.0.0.tgz#3906aa6f0e56fd012d73559f5f05c02e63ba18dd" integrity sha512-hZhjNrle/DMi1ziHwHy8LS0fYJtf+cID7fuG5+4h+Bk83xQaRDQT/DlqfL4hJYw3mxW6KTIxoODrhGnhqJebdQ== +"@testing-library/react@14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c" + integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" + +"@testing-library/user-event@14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.0.0.tgz#3906aa6f0e56fd012d73559f5f05c02e63ba18dd" + integrity sha512-hZhjNrle/DMi1ziHwHy8LS0fYJtf+cID7fuG5+4h+Bk83xQaRDQT/DlqfL4hJYw3mxW6KTIxoODrhGnhqJebdQ== + "@testing-library/user-event@14.5.1", "@testing-library/user-event@^14.4.0": version "14.5.1" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" @@ -13466,7 +13493,6 @@ version "0.0.7" resolved "https://registry.yarnpkg.com/@trendyol-js/openstack-swift-sdk/-/openstack-swift-sdk-0.0.7.tgz#45ba1dbfd1ccd3df263b25981a4d563943e1766d" integrity sha512-N3jYUiqwNT4KajMPQJkFn6FrVCdFcnyRvphGHCdyfvxf3PxZbEDV2mDjLQ+qNTLWsDS3hEfE63KISOpM2Cuo6g== - dependencies: agentkeepalive "^4.1.4" axios "^1.0.0" axios-cached-dns-resolve "0.5.2" @@ -14011,8 +14037,8 @@ "@types/express-serve-static-core" "^4.17.33" "@types/qs" "*" "@types/serve-static" "*" - "@types/express@^4.17.14", "@types/express@^4.7.0": +"@types/express@^4.7.0": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -14506,11 +14532,11 @@ integrity sha512-x347Gy+ZG2dCMiWEHRwiIJ4uMlTmxW7yheRMSQbNf0mFkmXhnYE5nvRdt6SbnT6Z2dp7KPIr3CSvBgSiTIr9zw== dependencies: "@types/react" "*" - "@types/react-dom@18.2.18", "@types/react-dom@^18", "@types/react-dom@^18.0.0": version "18.2.19" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.19.tgz#b84b7c30c635a6c26c6a6dfbb599b2da9788be58" integrity sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA== + integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== dependencies: "@types/react" "*" @@ -14742,7 +14768,6 @@ dependencies: "@types/cookiejar" "*" "@types/node" "*" - "@types/supertest@2.0.12": version "2.0.12" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" @@ -14751,6 +14776,7 @@ "@types/superagent" "*" "@types/supertest@2.0.16": +"@types/supertest@2.0.16", "@types/supertest@^2.0.12": version "2.0.16" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.16.tgz#7a1294edebecb960d957bbe9b26002a2b7f21cd7" integrity sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg== @@ -16782,6 +16808,11 @@ bottleneck@^2.15.3: resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== +bowser@^1.7.3: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -18288,6 +18319,11 @@ core-js-pure@^3.23.3, core-js-pure@^3.30.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.33.0.tgz#938a28754b4d82017a7a8cbd2727b1abecc63591" integrity sha512-FKSIDtJnds/YFIEaZ4HszRX7hkxGpNKM7FC9aJ9WLJbSd3lD4vOltFuVIBLR8asSx9frkTSqL0dw90SKQxgKrg== +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA== + core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" @@ -18545,6 +18581,14 @@ css-declaration-sorter@^6.3.1: resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g== +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-in-js-utils@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" @@ -19942,7 +19986,7 @@ encodeurl@^1.0.2, encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.13: +encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -21335,6 +21379,19 @@ fbjs-css-vars@^1.0.0: resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== +fbjs@^0.8.12: + version "0.8.18" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.18.tgz#9835e0addb9aca2eff53295cd79ca1cfc7c9662a" + integrity sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA== + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.30" + fbjs@^3.0.0, fbjs@^3.0.1: version "3.0.5" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" @@ -22258,6 +22315,17 @@ github-slugger@^1.0.0: resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== +glamor@^2.20.40: + version "2.20.40" + resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.40.tgz#f606660357b7cf18dface731ad1a2cfa93817f05" + integrity sha512-DNXCd+c14N9QF8aAKrfl4xakPk5FdcFwmH7sD0qnC0Pr7xoZ5W9yovhUrY/dJc3psfGGXC58vqQyRtuskyUJxA== + dependencies: + fbjs "^0.8.12" + inline-style-prefixer "^3.0.6" + object-assign "^4.1.1" + prop-types "^15.5.10" + through "^2.3.8" + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -23259,7 +23327,7 @@ hyperlinker@^1.0.0: resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== -hyphenate-style-name@^1.0.3: +hyphenate-style-name@^1.0.2, hyphenate-style-name@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== @@ -23485,6 +23553,14 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +inline-style-prefixer@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz#8551b8e5b4d573244e66a34b04f7d32076a2b534" + integrity sha512-ne8XIyyqkRaNJ1JfL1NYzNdCNxq+MCBQhC8NgOQlzNm2vv3XxlP0VSLQUbSRCF6KPEoveCVEpayHoHzcMyZsMQ== + dependencies: + bowser "^1.7.3" + css-in-js-utils "^2.0.0" + inline-style-prefixer@^6.0.0: version "6.0.4" resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44" @@ -24192,6 +24268,14 @@ isomorphic-dompurify@^0.13.0: dompurify "^2.2.7" jsdom "^16.5.2" +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA== + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + isomorphic-form-data@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz#9f6adf1c4c61ae3aefd8f110ab60fb9b143d6cec" @@ -27927,7 +28011,6 @@ node-fetch-native@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.4.1.tgz#5a336e55b4e1b1e72b9927da09fecd2b374c9be5" integrity sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w== - node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -27935,6 +28018,7 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" + node-fetch@^2.0.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" @@ -31226,6 +31310,13 @@ react-moment@^1.1.3: resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-1.1.3.tgz#829b21dfb279aa6db47ce4f1ac2555af17a1bcdc" integrity sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA== +react-multi-progress@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-multi-progress/-/react-multi-progress-1.3.0.tgz#c36ee7a019357de30770b47e1bc072604439b6a5" + integrity sha512-uWwcDCBQNlccuyWUVYUBslVoGCvvFXO4GMsHZnlymvIjMMUeY+tjWdXyEtWgdVh3Qgz9mTdDMx8fHtX7bxLjyg== + dependencies: + glamor "^2.20.40" + react-redux@^7.2.0: version "7.2.9" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d" @@ -33759,8 +33850,8 @@ sucrase@^3.20.2: mz "^2.7.0" pirates "^4.0.1" ts-interface-checker "^0.1.9" - superagent@^8.0.0, superagent@^8.0.5: +superagent@^8.0.5, superagent@^8.1.2: version "8.1.2" resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== @@ -34199,7 +34290,7 @@ through2@^4.0.0: dependencies: readable-stream "3" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -34933,6 +35024,11 @@ typestyle@^2.4.0: csstype "3.0.10" free-style "3.1.0" +ua-parser-js@^0.7.30: + version "0.7.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" + integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== + ua-parser-js@^1.0.35: version "1.0.37" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" @@ -36275,6 +36371,11 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-fetch@>=0.10.0: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + whatwg-fetch@^3.6.0: version "3.6.19" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz#caefd92ae630b91c07345537e67f8354db470973"