final fixup

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2021-04-21 12:45:31 +02:00
parent 5fa7f67933
commit 87a82498b9
23 changed files with 205 additions and 264 deletions
@@ -283,9 +283,9 @@ metadata:
```
The value of this annotation controls the code-coverage backstage plugin. If set
to smc-only the plugin will only take into account files stored in source
control (e.g. ignoring generated code). If set to any other non-false:y value
all files covered by a coverage report will be taken into account.
to `scm-only`, the plugin will only take into account files stored in source
control (e.g. ignoring generated code). If set to `enabled`, all files covered
by a coverage report will be taken into account.
## Deprecated Annotations
+10 -6
View File
@@ -1,19 +1,23 @@
# code-coverage
# code-coverage-backend
This is the backend part of the code-coverage plugin. It takes care of processing various coverage formats and standardizing them into a single json format, used by the frontend.
This is the backend part of the `code-coverage` plugin. It takes care of processing various coverage formats and standardizing them into a single json format, used by the frontend.
## Configuring your entity
In order to use this plugin, you must set the `backstage.io/code-coverage` annotation.
```yaml
backstage.io/code-coverage: enabled
metadata:
annotations:
backstage.io/code-coverage: enabled
```
There's a feature to only include files that are in VCS in the coverage report, this is helpful to not count generated files for example. To enable this set the `backstage.io/code-coverage` annotation to `scm-only`.
```yaml
backstage.io/code-coverage: scm-only
metadata:
annotations:
backstage.io/code-coverage: scm-only
```
Note: It may be required to set the [`backstage.io/source-location` annotation](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location), however this should generally not be needed.
@@ -31,7 +35,7 @@ Example:
{
"links": [
{
"href": "http://localhost:7000/api/code-coverage/Component/default/entity-name",
"href": "http://localhost:7000/api/code-coverage/report?entity=component:default/entity-name",
"rel": "coverage"
}
]
@@ -49,7 +53,7 @@ Example:
{
"links": [
{
"href": "http://localhost:7000/api/code-coverage/Component/default/entity-name",
"href": "http://localhost:7000/api/code-coverage/report?entity=component:default/entity-name",
"rel": "coverage"
}
]
@@ -20,14 +20,23 @@
* @param {import('knex').Knex} knex
*/
exports.up = async function up(knex) {
// Note for the reader: the knex increments types automatically make it a
// primary column, whether you like it or not. That's why the id column is
// not marked as primary as one might have expected; it's only used for
// lookups by ID. Because, SQLite and MySQL don't return RETURNING on
// inserts ... so we want a manually generated key for lookups (an uuid),
// and also an index for ordering guarantees :)
await knex.schema.createTable('code_coverage', table => {
table.comment('The table of code coverage');
table
.uuid('id')
.primary()
.bigIncrements('index')
.notNullable()
.comment('The ID of the code coverage');
table.text('entity').notNullable().comment('entity string reference');
.comment('An insert counter to ensure ordering');
table.uuid('id').notNullable().comment('The ID of the code coverage');
table
.text('entity')
.notNullable()
.comment('The entity ref that this code coverage applies to');
table
.text('coverage')
.notNullable()
@@ -37,6 +46,9 @@ exports.up = async function up(knex) {
.defaultTo(knex.fn.now())
.notNullable()
.comment('The timestamp when this entry was created');
table.index('index', 'code_coverage_index_idx');
table.index('id', 'code_coverage_id_idx');
table.index('entity', 'code_coverage_entity_idx');
});
};
@@ -44,5 +56,10 @@ exports.up = async function up(knex) {
* @param {import('knex').Knex} knex
*/
exports.down = async function down(knex) {
await knex.schema.alterTable('code_coverage', table => {
table.dropIndex([], 'code_coverage_index_idx');
table.dropIndex([], 'code_coverage_id_idx');
table.dropIndex([], 'code_coverage_entity_idx');
});
await knex.schema.dropTable('code_coverage');
};
+6 -7
View File
@@ -19,13 +19,12 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.5.4",
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/backend-common": "^0.6.2",
"@backstage/catalog-client": "^0.3.10",
"@backstage/catalog-model": "^0.7.7",
"@backstage/config": "^0.1.4",
"@backstage/errors": "^0.1.1",
"@backstage/integration": "^0.5.0",
"@backstage/plugin-catalog-backend": "^0.6.5",
"@backstage/integration": "^0.5.1",
"@types/express": "^4.17.6",
"cross-fetch": "^3.0.6",
"express": "^4.17.1",
@@ -37,7 +36,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.2",
"@backstage/cli": "^0.6.8",
"@types/express-xml-bodyparser": "^0.3.2",
"@types/supertest": "^2.0.8",
"msw": "^0.21.2",
@@ -15,5 +15,3 @@
*/
export * from './service/router';
export * from './service/jsoncoverage-types';
@@ -13,17 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
getVoidLogger,
SingleConnectionDatabaseManager,
} from '@backstage/backend-common';
import { SingleConnectionDatabaseManager } from '@backstage/backend-common';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { ConfigReader } from '@backstage/config';
import {
CodeCoverageDatabase,
CodeCoverageStore,
} from './CodeCoverageDatabase';
import { JsonCodeCoverage } from './jsoncoverage-types';
import { JsonCodeCoverage } from './types';
const db = SingleConnectionDatabaseManager.fromConfig(
new ConfigReader({
@@ -103,10 +100,9 @@ let database: CodeCoverageStore;
describe('CodeCoverageDatabase', () => {
beforeAll(async () => {
const client = await db.getClient();
database = await CodeCoverageDatabase.create(client, getVoidLogger());
database.insertCodeCoverage(coverage[0]);
await new Promise(r => setTimeout(r, 1000));
database.insertCodeCoverage(coverage[1]);
database = await CodeCoverageDatabase.create(client);
await database.insertCodeCoverage(coverage[0]);
await database.insertCodeCoverage(coverage[1]);
});
describe('insertCodeCoverage', () => {
@@ -18,15 +18,15 @@ import { NotFoundError } from '@backstage/errors';
import { parseEntityName, stringifyEntityRef } from '@backstage/catalog-model';
import { Knex } from 'knex';
import { v4 as uuid } from 'uuid';
import { Logger } from 'winston';
import { aggregateCoverage } from './CoverageUtils';
import { JsonCodeCoverage, JsonCoverageHistory } from './jsoncoverage-types';
import { JsonCodeCoverage, JsonCoverageHistory } from './types';
export type RawDbCoverageRow = {
id: string;
entity: string;
coverage: string;
};
export interface CodeCoverageStore {
insertCodeCoverage(
coverage: JsonCodeCoverage,
@@ -39,44 +39,49 @@ const migrationsDir = resolvePackagePath(
'@backstage/plugin-code-coverage-backend',
'migrations',
);
export class CodeCoverageDatabase implements CodeCoverageStore {
static async create(knex: Knex, logger: Logger): Promise<CodeCoverageStore> {
static async create(knex: Knex): Promise<CodeCoverageStore> {
await knex.migrate.latest({
directory: migrationsDir,
});
return new CodeCoverageDatabase(knex, logger);
return new CodeCoverageDatabase(knex);
}
constructor(private readonly db: Knex, private readonly logger: Logger) {}
constructor(private readonly db: Knex) {}
async insertCodeCoverage(
coverage: JsonCodeCoverage,
): Promise<{ codeCoverageId: string }> {
const codeCoverageId = uuid();
this.logger.error(JSON.stringify(coverage.entity));
const entity = stringifyEntityRef({
kind: coverage.entity.kind,
namespace: coverage.entity.namespace,
name: coverage.entity.name,
});
await this.db<RawDbCoverageRow>('code_coverage').insert({
id: codeCoverageId,
entity: entity,
coverage: JSON.stringify(coverage),
});
return { codeCoverageId };
}
async getCodeCoverage(entity: string): Promise<JsonCodeCoverage> {
const [result] = await this.db<RawDbCoverageRow>('code_coverage')
.where({ entity: entity })
.orderBy('created_at', 'desc')
.orderBy('index', 'desc')
.limit(1)
.select();
if (!result) {
throw new NotFoundError(
`No coverage for entity '${JSON.stringify(entity)}' found`,
);
}
try {
return JSON.parse(result.coverage);
} catch (error) {
@@ -90,7 +95,7 @@ export class CodeCoverageDatabase implements CodeCoverageStore {
): Promise<JsonCoverageHistory> {
const res = await this.db<RawDbCoverageRow>('code_coverage')
.where({ entity: entity })
.orderBy('created_at', 'desc')
.orderBy('index', 'desc')
.limit(limit)
.select();
@@ -99,6 +104,7 @@ export class CodeCoverageDatabase implements CodeCoverageStore {
.map(c => aggregateCoverage(c));
const entityName = parseEntityName(entity);
return {
entity: {
name: entityName.name,
@@ -22,11 +22,7 @@ import {
stringifyEntityRef,
} from '@backstage/catalog-model';
import { ScmIntegration, ScmIntegrations } from '@backstage/integration';
import {
AggregateCoverage,
FileEntry,
JsonCodeCoverage,
} from './jsoncoverage-types';
import { AggregateCoverage, FileEntry, JsonCodeCoverage } from './types';
export const calculatePercentage = (
available: number,
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FileEntry } from '../jsoncoverage-types';
import { FileEntry } from '../types';
export interface Converter {
convert(xml: unknown, scmFiles: Array<string>): Array<FileEntry>;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BranchHit, FileEntry } from '../jsoncoverage-types';
import { BranchHit, FileEntry } from '../types';
import { CoberturaXML, InnerClass, LineHit } from './types';
import { Logger } from 'winston';
import { Converter } from './Converter';
@@ -63,7 +63,7 @@ export class Cobertura implements Converter {
});
const currentFile = scmFiles.find(f => f.endsWith(packageAndFilename));
this.logger.info(`matched ${packageAndFilename} to ${currentFile}`);
this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);
if (
scmFiles.length === 0 ||
(Object.keys(lineHits).length > 0 && currentFile)
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BranchHit, FileEntry } from '../jsoncoverage-types';
import { BranchHit, FileEntry } from '../types';
import { JacocoSourceFile, JacocoXML } from './types';
import { Logger } from 'winston';
import { Converter } from './Converter';
@@ -35,7 +35,7 @@ export class Jacoco implements Converter {
* Converts jacoco into shared json coverage format
*
* @param xml jacoco xml object
* @param scmFiles list of files that are commited to SCM
* @param scmFiles list of files that are committed to SCM
*/
convert(xml: JacocoXML, scmFiles: Array<string>): Array<FileEntry> {
const jscov: Array<FileEntry> = [];
@@ -64,7 +64,7 @@ export class Jacoco implements Converter {
const packageAndFilename = `${packageName}/${fileName}`;
const currentFile = scmFiles.find(f => f.endsWith(packageAndFilename));
this.logger.info(`matched ${packageAndFilename} to ${currentFile}`);
this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);
if (Object.keys(lineHits).length > 0 && currentFile) {
jscov.push({
filename: currentFile,
@@ -58,7 +58,6 @@ export const makeRouter = async (
const codeCoverageDatabase = await CodeCoverageDatabase.create(
await database.getClient(),
logger,
);
const codecovUrl = await discovery.getExternalBaseUrl('code-coverage');
const catalogApi = new CatalogClient({ discoveryApi: discovery });
@@ -147,8 +146,8 @@ export const makeRouter = async (
const scmTree = await urlReader.readTree(sourceLocation.target);
const scmFile = (await scmTree.files()).find(f => f.path === path);
if (!scmFile) {
res.status(400).json({
message: "couldn't find file in SCM",
res.status(404).json({
message: "Couldn't find file in SCM",
file: path,
scm: vcs.title,
});
@@ -157,7 +156,7 @@ export const makeRouter = async (
const content = await scmFile?.content();
if (!content) {
res.status(400).json({
message: "couldn't process content of file in SCM",
message: "Couldn't process content of file in SCM",
file: path,
scm: vcs.title,
});
@@ -187,7 +186,7 @@ export const makeRouter = async (
} else if (coverageType === 'cobertura') {
converter = new Cobertura(logger);
} else {
throw new NotFoundError(`unsupported coverage type '${coverageType}`);
throw new InputError(`Unsupported coverage type '${coverageType}`);
}
const {
@@ -22,9 +22,9 @@ import {
useHotMemoize,
} from '@backstage/backend-common';
import { Server } from 'http';
import knexFactory from 'knex';
import { Logger } from 'winston';
import { createRouter } from './router';
import { DatabaseManager } from '@backstage/plugin-catalog-backend';
export interface ServerOptions {
port: number;
@@ -38,13 +38,23 @@ export async function startStandaloneServer(
const logger = options.logger.child({ service: 'code-coverage-backend' });
const config = await loadBackendConfig({ logger, argv: process.argv });
const db = useHotMemoize(module, () =>
DatabaseManager.createInMemoryDatabaseConnection(),
);
const db = useHotMemoize(module, () => {
const knex = knexFactory({
client: 'sqlite3',
connection: ':memory:',
useNullAsDefault: true,
});
knex.client.pool.on('createSuccess', (_eventId: any, resource: any) => {
resource.run('PRAGMA foreign_keys = ON', () => {});
});
return knex;
});
logger.debug('Starting application server...');
const router = await createRouter({
database: { getClient: () => db },
database: { getClient: async () => db },
config,
discovery: SingleHostDiscovery.fromConfig(config),
urlReader: UrlReaders.default({ logger, config }),
+8 -4
View File
@@ -1,19 +1,23 @@
# code-coverage
This is the frontend part of the code-coverage plugin. It takes care of processing various coverage formats and standardizing them into a single json format, used by the frontend.
This is the frontend part of the code-coverage plugin. It displays code coverage summaries for your entities.
## Configuring your entity
In order to use this plugin, you must set the `backstage.io/code-coverage` annotation.
In order to use this plugin, you must set the `backstage.io/code-coverage` annotation on entities for which coverage ingestion has been enabled.
```yaml
backstage.io/code-coverage: enabled
metadata:
annotations:
backstage.io/code-coverage: enabled
```
There's a feature to only include files that are in VCS in the coverage report, this is helpful to not count generated files for example. To enable this set the `backstage.io/code-coverage` annotation to `scm-only`.
```yaml
backstage.io/code-coverage: scm-only
metadata:
annotations:
backstage.io/code-coverage: scm-only
```
Note: It may be required to set the [`backstage.io/source-location` annotation](https://backstage.io/docs/features/software-catalog/well-known-annotations#backstageiosource-location), however this should generally not be needed.
+4 -6
View File
@@ -20,13 +20,11 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/catalog-model": "^0.7.7",
"@backstage/config": "^0.1.4",
"@backstage/core": "^0.7.5",
"@backstage/core-api": "^0.2.12",
"@backstage/errors": "^0.1.0",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/plugin-code-coverage-backend": "^0.1.0",
"@backstage/errors": "^0.1.1",
"@backstage/plugin-catalog-react": "^0.1.4",
"@backstage/theme": "^0.2.5",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
+6 -23
View File
@@ -14,27 +14,10 @@
* limitations under the License.
*/
import { createApiRef, DiscoveryApi } from '@backstage/core';
import { EntityName, stringifyEntityRef } from '@backstage/catalog-model';
import {
JsonCodeCoverage,
JsonCoverageHistory,
} from '@backstage/plugin-code-coverage-backend';
import { createApiRef, DiscoveryApi } from '@backstage/core';
import { ResponseError } from '@backstage/errors';
export class FetchError extends Error {
get name(): string {
return this.constructor.name;
}
static async forResponse(resp: Response): Promise<FetchError> {
return new FetchError(
`Request failed with status code ${
resp.status
}.\nReason: ${await resp.text()}`,
);
}
}
import { JsonCodeCoverage, JsonCoverageHistory } from './types';
export type CodeCoverageApi = {
discovery: DiscoveryApi;
@@ -79,7 +62,7 @@ export class CodeCoverageRestApi implements CodeCoverageApi {
async getCoverageForEntity(
entityName: EntityName,
): Promise<JsonCodeCoverage> {
const entity = encodeURI(stringifyEntityRef(entityName));
const entity = encodeURIComponent(stringifyEntityRef(entityName));
return (await this.fetch<JsonCodeCoverage>(
`/report?entity=${entity}`,
)) as JsonCodeCoverage;
@@ -89,7 +72,7 @@ export class CodeCoverageRestApi implements CodeCoverageApi {
entityName: EntityName,
filePath: string,
): Promise<string> {
const entity = encodeURI(stringifyEntityRef(entityName));
const entity = encodeURIComponent(stringifyEntityRef(entityName));
return await this.fetch<string>(
`/file-content?entity=${entity}&path=${encodeURI(filePath)}`,
);
@@ -99,11 +82,11 @@ export class CodeCoverageRestApi implements CodeCoverageApi {
entityName: EntityName,
limit?: number,
): Promise<JsonCoverageHistory> {
const entity = encodeURI(stringifyEntityRef(entityName));
const entity = encodeURIComponent(stringifyEntityRef(entityName));
const hasValidLimit = limit && limit > 0;
return (await this.fetch<JsonCoverageHistory>(
`/history?entity=${entity}${
hasValidLimit ? `&limit=${encodeURI(`${limit}`)}` : ''
hasValidLimit ? `&limit=${encodeURIComponent(String(limit))}` : ''
}`,
)) as JsonCoverageHistory;
}
@@ -14,7 +14,9 @@
* limitations under the License.
*/
import React from 'react';
import { Progress, ResponseErrorPanel, useApi } from '@backstage/core';
import { useEntity } from '@backstage/plugin-catalog-react';
import { BackstageTheme } from '@backstage/theme';
import {
Box,
Card,
@@ -23,27 +25,24 @@ import {
makeStyles,
Typography,
} from '@material-ui/core';
import TrendingDownIcon from '@material-ui/icons/TrendingDown';
import TrendingFlatIcon from '@material-ui/icons/TrendingFlat';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import { Alert } from '@material-ui/lab';
import { ClassNameMap } from '@material-ui/styles';
import React from 'react';
import { useAsync } from 'react-use';
import {
LineChart,
XAxis,
YAxis,
Tooltip,
CartesianGrid,
Legend,
Line,
CartesianGrid,
LineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { useAsync } from 'react-use';
import { useApi } from '@backstage/core-api';
import { useEntity } from '@backstage/plugin-catalog-react';
import { codeCoverageApiRef } from '../../api';
import { Progress, ResponseErrorPanel } from '@backstage/core';
import { Alert } from '@material-ui/lab';
import TrendingDownIcon from '@material-ui/icons/TrendingDown';
import TrendingUpIcon from '@material-ui/icons/TrendingUp';
import TrendingFlatIcon from '@material-ui/icons/TrendingFlat';
import { BackstageTheme } from '@backstage/theme';
import { ClassNameMap } from '@material-ui/styles';
type Coverage = 'line' | 'branch';
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
// styles
import React from 'react';
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
@@ -64,7 +63,8 @@ type CodeRowProps = {
lineContent: string;
lineHits?: number | null;
};
const CodeRow = ({
export const CodeRow = ({
lineNumber,
lineContent,
lineHits = null,
@@ -98,5 +98,3 @@ const CodeRow = ({
</tr>
);
};
export default CodeRow;
@@ -14,17 +14,16 @@
* limitations under the License.
*/
import React from 'react';
import { useApi } from '@backstage/core-api';
import { Progress, ResponseErrorPanel, useApi } from '@backstage/core';
import { useEntity } from '@backstage/plugin-catalog-react';
import { makeStyles, Paper } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import React from 'react';
import { useAsync } from 'react-use';
import { codeCoverageApiRef } from '../../api';
import { Progress, ResponseErrorPanel } from '@backstage/core';
import { Alert } from '@material-ui/lab';
import { makeStyles, Paper } from '@material-ui/core';
import { FileEntry } from '../../types';
import { CodeRow } from './CodeRow';
import { highlightLines } from './Highlighter';
import CoverageRow from './CoverageRow';
import { FileEntry } from '@backstage/plugin-code-coverage-backend';
type Props = {
filename: string;
@@ -61,7 +60,7 @@ const FormattedLines = ({
{highlightedLines.map((lineContent, idx) => {
const line = idx + 1;
return (
<CoverageRow
<CodeRow
key={line}
lineNumber={line}
lineContent={lineContent}
@@ -14,14 +14,14 @@
* limitations under the License.
*/
import { useApi } from '@backstage/core-api';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
Progress,
ResponseErrorPanel,
Table,
TableColumn,
useApi,
} from '@backstage/core';
import { useEntity } from '@backstage/plugin-catalog-react';
import {
Box,
Card,
@@ -30,13 +30,13 @@ import {
Modal,
Tooltip,
} from '@material-ui/core';
import DescriptionIcon from '@material-ui/icons/Description';
import { Alert } from '@material-ui/lab';
import React, { Fragment, useEffect, useState } from 'react';
import { useAsync } from 'react-use';
import { codeCoverageApiRef } from '../../api';
import DescriptionIcon from '@material-ui/icons/Description';
import { FileEntry } from '../../types';
import { FileContent } from './FileContent';
import { Alert } from '@material-ui/lab';
import { FileEntry } from '@backstage/plugin-code-coverage-backend';
type FileStructureObject = Record<string, any>;
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EntityName } from '@backstage/catalog-model';
export type JsonCodeCoverage = {
metadata: CoverageMetadata;
entity: EntityName;
files: Array<FileEntry>;
};
export type JsonCoverageHistory = {
entity: EntityName;
history: Array<AggregateCoverage>;
};
export type CoverageHistory = {
line: {
available: number;
covered: number;
};
branch: BranchHit;
};
export type CoverageMetadata = {
vcs: {
type: string;
location: string;
};
generationTime: number;
};
export type BranchHit = {
covered: number;
missed: number;
available: number;
};
export type FileEntry = {
filename: string;
lineHits: Record<number, number>;
branchHits: Record<number, BranchHit>;
};
export type AggregateCoverage = {
timestamp: number;
line: {
available: number;
covered: number;
missed: number;
percentage: number;
};
branch: {
available: number;
covered: number;
missed: number;
percentage: number;
};
};
+1 -137
View File
@@ -1616,7 +1616,7 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.13.10"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
@@ -1679,95 +1679,6 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
"@backstage/backend-common@^0.5.4":
version "0.5.6"
resolved "https://registry.npmjs.org/@backstage/backend-common/-/backend-common-0.5.6.tgz#e49840eaa6738f6697319ccdfff04ce40cc54b72"
integrity sha512-BzKlcTZLAVDpH87fi2TCWIFO9DfVdaPIIZ8gZSorCuLLErG4X7CS/zQiujJZ/zD5HGtO2QB57fuRkwjBMhJhhw==
dependencies:
"@backstage/cli-common" "^0.1.1"
"@backstage/config" "^0.1.2"
"@backstage/config-loader" "^0.5.1"
"@backstage/integration" "^0.5.1"
"@octokit/rest" "^18.0.12"
"@types/cors" "^2.8.6"
"@types/dockerode" "^3.2.1"
"@types/express" "^4.17.6"
archiver "^5.0.2"
compression "^1.7.4"
concat-stream "^2.0.0"
cors "^2.8.5"
cross-fetch "^3.0.6"
dockerode "^3.2.1"
express "^4.17.1"
express-promise-router "^3.0.3"
fs-extra "^9.0.1"
git-url-parse "^11.4.4"
helmet "^4.0.0"
isomorphic-git "^1.8.0"
knex "^0.95.1"
lodash "^4.17.15"
logform "^2.1.1"
minimatch "^3.0.4"
minimist "^1.2.5"
morgan "^1.10.0"
selfsigned "^1.10.7"
stoppable "^1.1.0"
tar "^6.0.5"
unzipper "^0.10.11"
winston "^3.2.1"
"@backstage/config-loader@^0.5.1":
version "0.5.1"
resolved "https://registry.npmjs.org/@backstage/config-loader/-/config-loader-0.5.1.tgz#a80f2047e209d2f41b17ad715c632ab142385b39"
integrity sha512-PmwXERs5BIf77sUIg3Fhp/elkR6ELj0czmpUgP4cuEdtazi0WcmdjCUC2DjgsKJWvYggsL1o372QKHh/M5AMTQ==
dependencies:
"@backstage/cli-common" "^0.1.1"
"@backstage/config" "^0.1.1"
ajv "^7.0.3"
fs-extra "^9.0.0"
json-schema "^0.2.5"
json-schema-merge-allof "^0.7.0"
typescript-json-schema "^0.47.0"
yaml "^1.9.2"
yup "^0.29.3"
"@backstage/plugin-catalog-backend@^0.6.5":
version "0.6.7"
resolved "https://registry.npmjs.org/@backstage/plugin-catalog-backend/-/plugin-catalog-backend-0.6.7.tgz#97d0ed6db6aeb04e09ed0fee05e76bacf66cc179"
integrity sha512-Ey9PXyUjmi8IxQNSXBhF6Sl2SmZ/iXCFHXnffH8XCdBFTZS8HDRE2tSOlIIU4XQKd6lJVuAGTekas2DkaS9/lA==
dependencies:
"@azure/msal-node" "^1.0.0-beta.3"
"@backstage/backend-common" "^0.6.0"
"@backstage/catalog-model" "^0.7.4"
"@backstage/config" "^0.1.4"
"@backstage/errors" "^0.1.1"
"@backstage/integration" "^0.5.1"
"@backstage/plugin-search-backend-node" "^0.1.2"
"@backstage/search-common" "^0.1.1"
"@octokit/graphql" "^4.5.8"
"@types/express" "^4.17.6"
"@types/ldapjs" "^1.0.9"
aws-sdk "^2.840.0"
codeowners-utils "^1.0.2"
core-js "^3.6.5"
cross-fetch "^3.0.6"
express "^4.17.1"
express-promise-router "^4.1.0"
fs-extra "^9.0.0"
git-url-parse "^11.4.4"
glob "^7.1.6"
knex "^0.95.1"
ldapjs "^2.2.0"
lodash "^4.17.15"
morgan "^1.10.0"
p-limit "^3.0.2"
qs "^6.9.4"
uuid "^8.0.0"
winston "^3.2.1"
yaml "^1.9.2"
yn "^4.0.0"
yup "^0.29.3"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -5609,20 +5520,6 @@
lz-string "^1.4.4"
pretty-format "^26.6.2"
"@testing-library/dom@^7.22.3":
version "7.30.3"
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.3.tgz#779ea9bbb92d63302461800a388a5a890ac22519"
integrity sha512-7JhIg2MW6WPwyikH2iL3o7z+FTVgSOd2jqCwTAHqK7Qal2gRRYiUQyURAxtbK9VXm/UTyG9bRihv8C5Tznr2zw==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
"@types/aria-query" "^4.2.0"
aria-query "^4.2.2"
chalk "^4.1.0"
dom-accessibility-api "^0.5.4"
lz-string "^1.4.4"
pretty-format "^26.6.2"
"@testing-library/jest-dom@^5.10.1":
version "5.11.9"
resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz#e6b3cd687021f89f261bd53cbe367041fbd3e975"
@@ -5653,14 +5550,6 @@
"@babel/runtime" "^7.5.4"
"@types/testing-library__react-hooks" "^3.4.0"
"@testing-library/react@^10.4.1":
version "10.4.9"
resolved "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz#9faa29c6a1a217bf8bbb96a28bd29d7a847ca150"
integrity sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==
dependencies:
"@babel/runtime" "^7.10.3"
"@testing-library/dom" "^7.22.3"
"@testing-library/react@^11.2.5":
version "11.2.6"
resolved "https://registry.npmjs.org/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b"
@@ -6439,11 +6328,6 @@
resolved "https://registry.npmjs.org/@types/node/-/node-10.17.35.tgz#58058f29b870e6ae57b20e4f6e928f02b7129f56"
integrity sha512-gXx7jAWpMddu0f7a+L+txMplp3FnHl53OhQIF9puXKq3hDGY/GjH+MF04oWnV/adPSCrbtHumDCFwzq2VhltWA==
"@types/node@^12.0.0":
version "12.20.10"
resolved "https://registry.npmjs.org/@types/node/-/node-12.20.10.tgz#4dcb8a85a8f1211acafb88d72fafc7e3d2685583"
integrity sha512-TxCmnSSppKBBOzYzPR2BR25YlX5Oay8z2XGwFBInuA/Co0V9xJhLlW4kjbxKtgeNo3NOMbQP1A5Rc03y+XecPw==
"@types/node@^12.7.1":
version "12.12.58"
resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c"
@@ -12975,15 +12859,6 @@ expect@^26.6.2:
jest-message-util "^26.6.2"
jest-regex-util "^26.0.0"
express-promise-router@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/express-promise-router/-/express-promise-router-3.0.3.tgz#5e6d22a5a3f013d71833172fe8d7ab780c3f6b70"
integrity sha1-Xm0ipaPwE9cYMxcv6NereAw/a3A=
dependencies:
is-promise "^2.1.0"
lodash.flattendeep "^4.0.0"
methods "^1.0.0"
express-promise-router@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/express-promise-router/-/express-promise-router-4.1.0.tgz#79160e145c27610ba411bceb0552a36f11dbab4f"
@@ -25778,17 +25653,6 @@ typedarray@^0.0.6:
resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript-json-schema@^0.47.0:
version "0.47.0"
resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.47.0.tgz#84dde5460b127c6774da81bf70b23c7e04857b13"
integrity sha512-A6NVwSOTSsNDHfaqDcDeKwwyXEeKqBHoAr20jcetnYj4e8C6zVFofAVhAuwsBXCRYiWEE/lyHrcxpsSpbIk0Mg==
dependencies:
"@types/json-schema" "^7.0.6"
glob "^7.1.6"
json-stable-stringify "^1.0.1"
typescript "^4.1.3"
yargs "^16.2.0"
typescript-json-schema@^0.49.0:
version "0.49.0"
resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.49.0.tgz#442f6347ca85fb0d9811f217fb0d6537b68734b3"