From 1b393e83a02a1e4b114784845d7818ed8238a476 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 | 275 +++++++++++++++++- 36 files changed, 1096 insertions(+), 9 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 9b5ddbd591..1498988d87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4169,6 +4169,36 @@ zen-observable "^0.10.0" zod "^3.22.4" +"@backstage/core-app-api@^1.11.3": + version "1.11.3" + resolved "https://registry.yarnpkg.com/@backstage/core-app-api/-/core-app-api-1.11.3.tgz#49d97c6fd1ed051b4520e8afb1400951d93d587f" + integrity sha512-GVs4M5SarJXXW4MByqRQIHTb6B3RHsNsdoLVmd9BmdKJ9hoPh+UFQTUoXhQyO9sbbGLutiQEV5L6kZAaXkW9Fw== + dependencies: + "@backstage/config" "^1.1.1" + "@backstage/core-plugin-api" "^1.8.2" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.7" + "@types/prop-types" "^15.7.3" + "@types/react" "^16.13.1 || ^17.0.0" + history "^5.0.0" + i18next "^22.4.15" + lodash "^4.17.21" + prop-types "^15.7.2" + react-use "^17.2.4" + zen-observable "^0.10.0" + zod "^3.22.4" + +"@backstage/core-compat-api@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@backstage/core-compat-api/-/core-compat-api-0.1.1.tgz#e7c0f0ae47a634de2df35decf58776c4308f1bfd" + integrity sha512-Cv53pBB4Q6nRtTeBkpMXBowK8cc7mGiDjn0NHbdz1pzdUpXX2hXm6CwT/2IHtpJJTtML3TiwIyueyDsQkR0c6g== + dependencies: + "@backstage/core-app-api" "^1.11.3" + "@backstage/core-plugin-api" "^1.8.2" + "@backstage/frontend-plugin-api" "^0.5.0" + "@backstage/version-bridge" "^1.0.7" + "@types/react" "^16.13.1 || ^17.0.0" + "@backstage/core-components@0.13.6", "@backstage/core-components@^0.13.6": version "0.13.6" resolved "https://registry.yarnpkg.com/@backstage/core-components/-/core-components-0.13.6.tgz#c34b9ec741c993db7a63634bc25b9d23e4939e6b" @@ -5111,6 +5141,39 @@ react-use "^17.2.4" zen-observable "^0.10.0" +"@backstage/plugin-catalog@^1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@backstage/plugin-catalog/-/plugin-catalog-1.16.1.tgz#be2f7d726a0283739c46a1f28bda9fa1c0fca3ef" + integrity sha512-h0u8O6A+rR4NAvXzCIeMX56etWTbEmBRQx/QHpY7ZfkwxW601OvTKlT0v7t1wOTW9NbXlO4STyoHtrHc5a1geA== + dependencies: + "@backstage/catalog-client" "^1.5.2" + "@backstage/catalog-model" "^1.4.3" + "@backstage/core-compat-api" "^0.1.1" + "@backstage/core-components" "^0.13.10" + "@backstage/core-plugin-api" "^1.8.2" + "@backstage/errors" "^1.2.3" + "@backstage/frontend-plugin-api" "^0.5.0" + "@backstage/integration-react" "^1.1.23" + "@backstage/plugin-catalog-common" "^1.0.20" + "@backstage/plugin-catalog-react" "^1.9.3" + "@backstage/plugin-permission-react" "^0.4.19" + "@backstage/plugin-scaffolder-common" "^1.4.5" + "@backstage/plugin-search-common" "^1.2.10" + "@backstage/plugin-search-react" "^1.7.5" + "@backstage/types" "^1.1.1" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.61" + "@mui/utils" "^5.14.15" + "@types/react" "^16.13.1 || ^17.0.0" + dataloader "^2.0.0" + expiry-map "^2.0.0" + history "^5.0.0" + lodash "^4.17.21" + pluralize "^8.0.0" + react-use "^17.2.4" + zen-observable "^0.10.0" + "@backstage/plugin-events-backend@^0.2.15": version "0.2.15" resolved "https://registry.yarnpkg.com/@backstage/plugin-events-backend/-/plugin-events-backend-0.2.15.tgz#14e86ff7680f54846485231582c4714193cda187" @@ -5934,6 +5997,26 @@ qs "^6.9.4" react-use "^17.3.2" +"@backstage/plugin-search-react@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@backstage/plugin-search-react/-/plugin-search-react-1.7.5.tgz#2ee2079db75a988965d20a99280f1533351b1007" + integrity sha512-c69LMiMLDxHBd21g+MXjm3vzcAUMyjLxtFtsjfm43eikj0nf0eUxwzVKua/QuHVtjsvMA8pQmHBGCGCJgqWWLw== + dependencies: + "@backstage/core-components" "^0.13.10" + "@backstage/core-plugin-api" "^1.8.2" + "@backstage/frontend-plugin-api" "^0.5.0" + "@backstage/plugin-search-common" "^1.2.10" + "@backstage/theme" "^0.5.0" + "@backstage/types" "^1.1.1" + "@backstage/version-bridge" "^1.0.7" + "@material-ui/core" "^4.12.2" + "@material-ui/icons" "^4.9.1" + "@material-ui/lab" "4.0.0-alpha.61" + "@types/react" "^16.13.1 || ^17.0.0" + lodash "^4.17.21" + qs "^6.9.4" + react-use "^17.3.2" + "@backstage/plugin-search@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@backstage/plugin-search/-/plugin-search-1.4.1.tgz#1638bdeeb8debc0a62690125065f5649d0e2bc71" @@ -8138,7 +8221,7 @@ "@types/set-cookie-parser" "^2.4.0" set-cookie-parser "^2.4.6" -"@mswjs/interceptors@^0.17.10": +"@mswjs/interceptors@^0.17.10", "@mswjs/interceptors@^0.17.5": version "0.17.10" resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.17.10.tgz#857b41f30e2b92345ed9a4e2b1d0a08b8b6fcad4" integrity sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw== @@ -13497,6 +13580,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" @@ -13514,6 +13611,20 @@ "@testing-library/dom" "^8.0.0" "@types/react-dom" "<18.0.0" +"@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" @@ -14060,7 +14171,7 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@^4.7.0": +"@types/express@^*", "@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== @@ -14554,7 +14665,7 @@ dependencies: "@types/react" "*" -"@types/react-dom@17.0.21", "@types/react-dom@<18.0.0", "@types/react-dom@^17", "@types/react-dom@^17.0.21": +"@types/react-dom@17.0.21", "@types/react-dom@<18.0.0", "@types/react-dom@^17", "@types/react-dom@^17.0.21", "@types/react-dom@^18.0.0": version "17.0.25" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== @@ -14800,6 +14911,13 @@ "@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" + integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== + dependencies: + "@types/superagent" "*" + "@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" @@ -16651,6 +16769,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" @@ -17153,6 +17276,14 @@ chalk@2.4.2, chalk@^2.3.2, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -18133,6 +18264,11 @@ core-js-pure@^3.23.3, core-js-pure@^3.30.2, core-js-pure@^3.6.5: 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" @@ -18383,6 +18519,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" @@ -19737,7 +19881,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== @@ -20952,6 +21096,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" @@ -21787,6 +21944,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" @@ -22114,7 +22282,7 @@ graphql-ws@^5.4.1: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.14.1.tgz#d05dba9c2cbf1582c990a2dfec4b8f6a55d99da4" integrity sha512-aqkls1espsygP1PfkAuuLIV96IbztQ6EaADse97pw8wRIMT3+AL/OYfS8V2iCRkc0gzckitoDRGCQEdnySggiA== -graphql@^16.0.0, graphql@^16.8.1: +"graphql@^15.0.0 || ^16.0.0", graphql@^16.0.0, graphql@^16.8.1: version "16.8.1" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== @@ -22345,6 +22513,11 @@ headers-polyfill@3.2.5: resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.2.5.tgz#6e67d392c9d113d37448fe45014e0afdd168faed" integrity sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA== +headers-polyfill@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.3.0.tgz#67c6ef7b72d4c8cac832ad5936f5b3a56e7b705a" + integrity sha512-5e57etwBpNcDc0b6KCVWEh/Ro063OxPvzVimUdM0/tsYM/T7Hfy3kknIGj78SFTOhNd8AZY41U8mOHoO4LzmIQ== + heap@^0.2.6: version "0.2.7" resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" @@ -22727,7 +22900,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== @@ -22948,6 +23121,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" @@ -23361,7 +23542,7 @@ is-negative-zero@^2.0.2: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== -is-node-process@^1.2.0: +is-node-process@^1.0.1, is-node-process@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== @@ -23496,6 +23677,11 @@ is-stream-ended@^0.1.4: resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -23629,6 +23815,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" @@ -27094,6 +27288,31 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msw@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/msw/-/msw-1.0.0.tgz#4f8e63aa23912561a63b99ff560a089da6969418" + integrity sha512-8QVa1RAN/Nzbn/tKmtimJ+b2M1QZOMdETQW7/1TmBOZ4w+wJojfxuh1Hj5J4FYdBgZWW/TK4CABUOlOM4OjTOA== + dependencies: + "@mswjs/cookies" "^0.2.2" + "@mswjs/interceptors" "^0.17.5" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + chalk "4.1.1" + chokidar "^3.4.2" + cookie "^0.4.2" + graphql "^15.0.0 || ^16.0.0" + headers-polyfill "^3.1.0" + inquirer "^8.2.0" + is-node-process "^1.0.1" + js-levenshtein "^1.1.6" + node-fetch "^2.6.7" + outvariant "^1.3.0" + path-to-regexp "^6.2.0" + strict-event-emitter "^0.4.3" + type-fest "^2.19.0" + yargs "^17.3.1" + msw@1.3.2, msw@^1.0.0: version "1.3.2" resolved "https://registry.yarnpkg.com/msw/-/msw-1.3.2.tgz#35e0271293e893fc3c55116e90aad5d955c66899" @@ -27395,6 +27614,14 @@ node-fetch-native@^1.4.0: resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.4.1.tgz#5a336e55b4e1b1e72b9927da09fecd2b374c9be5" integrity sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w== +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + 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" @@ -28507,6 +28734,11 @@ outvariant@^1.2.1, outvariant@^1.4.0: resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.0.tgz#e742e4bda77692da3eca698ef5bfac62d9fba06e" integrity sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw== +outvariant@^1.3.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.2.tgz#f54f19240eeb7f15b28263d5147405752d8e2066" + integrity sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ== + p-all@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-all/-/p-all-2.1.0.tgz#91419be56b7dee8fe4c5db875d55e0da084244a0" @@ -30621,6 +30853,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" @@ -33087,7 +33326,7 @@ sucrase@^3.20.2: pirates "^4.0.1" ts-interface-checker "^0.1.9" -superagent@^8.0.5, superagent@^8.1.2: +superagent@^8.0.0, 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== @@ -33103,6 +33342,14 @@ superagent@^8.0.5, superagent@^8.1.2: qs "^6.11.0" semver "^7.3.8" +supertest@6.2.4: + version "6.2.4" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.2.4.tgz#3dcebe42f7fd6f28dd7ac74c6cba881f7101b2f0" + integrity sha512-M8xVnCNv+q2T2WXVzxDECvL2695Uv2uUj2O0utxsld/HRyJvOU8W9f1gvsYxSNU4wmIe0/L/ItnpU4iKq0emDA== + dependencies: + methods "^1.1.2" + superagent "^8.0.0" + supertest@6.3.3: version "6.3.3" resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" @@ -33558,7 +33805,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== @@ -34271,6 +34518,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" @@ -35548,6 +35800,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"