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
@@ -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 }),