minor improvements to the coverage backend

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2022-12-22 13:39:43 +01:00
parent d1df595da3
commit dcfdaeccd3
6 changed files with 40 additions and 12 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-code-coverage-backend': patch
---
`RouterOptions` now accepts an optional `catalogApi` argument, allowing custom catalog clients to be used. This is leveraged in the local standalone development runner to pass in a catalog client that returns fake data.
@@ -209,7 +209,7 @@ describe('CodeCoverageUtils', () => {
} catch (error: any) {
err = error;
}
expect(err?.message).toEqual('Content-Type missing');
expect(err?.message).toEqual('Content-Type header missing');
});
it('rejects unsupported content type', () => {
@@ -223,7 +223,9 @@ describe('CodeCoverageUtils', () => {
} catch (error: any) {
err = error;
}
expect(err?.message).toEqual('Illegal Content-Type');
expect(err?.message).toEqual(
'Content-Type header "application/json" not supported, expected "text/xml" possibly followed by a charset',
);
});
it('parses the body', () => {
@@ -162,9 +162,11 @@ export class CoverageUtils {
validateRequestBody(req: Request) {
const contentType = req.headers['content-type'];
if (!contentType) {
throw new InputError('Content-Type missing');
throw new InputError('Content-Type header missing');
} else if (!contentType.match(/^text\/xml($|;)/)) {
throw new InputError('Illegal Content-Type');
throw new InputError(
`Content-Type header "${contentType}" not supported, expected "text/xml" possibly followed by a charset`,
);
}
const body = req.body;
if (!body) {
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BranchHit, FileEntry } from '../types';
import { JacocoSourceFile, JacocoXML } from './types';
import { Logger } from 'winston';
@@ -46,6 +46,7 @@ export interface RouterOptions {
database: PluginDatabaseManager;
urlReader: UrlReader;
logger: Logger;
catalogApi?: CatalogApi;
}
export interface CodeCoverageApi {
@@ -59,7 +60,8 @@ export const makeRouter = async (
const codeCoverageDatabase = await CodeCoverageDatabase.create(database);
const codecovUrl = await discovery.getExternalBaseUrl('code-coverage');
const catalogApi: CatalogApi = new CatalogClient({ discoveryApi: discovery });
const catalogApi =
options.catalogApi ?? new CatalogClient({ discoveryApi: discovery });
const scm = ScmIntegrations.fromConfig(config);
const router = Router();
@@ -167,10 +169,10 @@ export const makeRouter = async (
* /report?entity=component:default/mycomponent&coverageType=cobertura
*/
router.post('/report', async (req, res) => {
const { entity, coverageType } = req.query;
const entityLookup = await catalogApi.getEntityByRef(entity as string);
if (!entityLookup) {
throw new NotFoundError(`No entity found matching ${entity}`);
const { entity: entityRef, coverageType } = req.query;
const entity = await catalogApi.getEntityByRef(entityRef as string);
if (!entity) {
throw new NotFoundError(`No entity found matching ${entityRef}`);
}
let converter: Converter;
@@ -185,7 +187,7 @@ export const makeRouter = async (
}
const { sourceLocation, vcs, scmFiles, body } =
await utils.processCoveragePayload(entityLookup, req);
await utils.processCoveragePayload(entity, req);
const files = converter.convert(body, scmFiles);
if (!files || files.length === 0) {
@@ -193,7 +195,7 @@ export const makeRouter = async (
}
const coverage = await utils.buildCoverage(
entityLookup,
entity,
sourceLocation,
vcs,
files,
@@ -204,7 +206,7 @@ export const makeRouter = async (
links: [
{
rel: 'coverage',
href: `${codecovUrl}/report?entity=${entity}`,
href: `${codecovUrl}/report?entity=${entityRef}`,
},
],
});
@@ -21,6 +21,8 @@ import {
UrlReaders,
useHotMemoize,
} from '@backstage/backend-common';
import { CatalogApi } from '@backstage/catalog-client';
import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';
import { Server } from 'http';
import knexFactory from 'knex';
import { Logger } from 'winston';
@@ -52,13 +54,27 @@ export async function startStandaloneServer(
return knex;
});
const catalogApi = {
async getEntityByRef(entityRef: string | CompoundEntityRef) {
const { kind, namespace, name } = parseEntityRef(entityRef);
return {
apiVersion: 'backstage.io/v1alpha1',
kind,
metadata: { name, namespace },
spec: {},
};
},
} as Partial<CatalogApi> as CatalogApi;
logger.debug('Starting application server...');
const router = await createRouter({
database: { getClient: async () => db },
config,
discovery: SingleHostDiscovery.fromConfig(config),
urlReader: UrlReaders.default({ logger, config }),
logger,
catalogApi,
});
let service = createServiceBuilder(module)