@backstage-community/plugin-code-coverage-backend 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +4 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/CodeCoverageDatabase.cjs.js.map +1 -1
- package/dist/service/CoverageUtils.cjs.js.map +1 -1
- package/dist/service/converter/cobertura.cjs.js.map +1 -1
- package/dist/service/converter/jacoco.cjs.js.map +1 -1
- package/dist/service/converter/lcov.cjs.js.map +1 -1
- package/dist/service/router.cjs.js.map +1 -1
- package/package.json +17 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @backstage-community/plugin-code-coverage-backend
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 02e39fa: Backstage version bump to v1.34.1
|
|
8
|
+
|
|
9
|
+
## 0.3.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 37bd870: Deprecated `createRouter` and its router options in favour of the new backend system.
|
|
14
|
+
|
|
3
15
|
## 0.3.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
|
5
5
|
import { DiscoveryService, DatabaseService, UrlReaderService, LoggerService, AuthService, HttpAuthService } from '@backstage/backend-plugin-api';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
+
* @deprecated Please migrate to the new backend system as this will be removed in the future.
|
|
9
|
+
*
|
|
8
10
|
* Options for {@link createRouter}.
|
|
9
11
|
*
|
|
10
12
|
* @public
|
|
@@ -20,6 +22,8 @@ interface RouterOptions {
|
|
|
20
22
|
httpAuth?: HttpAuthService;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
25
|
+
* @deprecated Please migrate to the new backend system as this will be removed in the future.
|
|
26
|
+
*
|
|
23
27
|
* Creates a code-coverage plugin backend router.
|
|
24
28
|
*
|
|
25
29
|
* @public
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\n\n/**\n * Code coverage backend plugin\n *\n * @public\n */\nexport const codeCoveragePlugin = createBackendPlugin({\n pluginId: 'code-coverage',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n urlReader: coreServices.urlReader,\n httpRouter: coreServices.httpRouter,\n discovery: coreServices.discovery,\n database: coreServices.database,\n },\n async init({\n config,\n logger,\n urlReader,\n httpRouter,\n discovery,\n database,\n }) {\n httpRouter.use(\n await createRouter({\n config,\n logger,\n urlReader,\n discovery,\n database,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","createRouter"],"mappings":";;;;;AA0BO,MAAM,qBAAqBA,oCAAoB,CAAA;AAAA,EACpD,QAAU,EAAA,eAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,UAAUA,6BAAa,CAAA
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\n\n/**\n * Code coverage backend plugin\n *\n * @public\n */\nexport const codeCoveragePlugin = createBackendPlugin({\n pluginId: 'code-coverage',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n urlReader: coreServices.urlReader,\n httpRouter: coreServices.httpRouter,\n discovery: coreServices.discovery,\n database: coreServices.database,\n },\n async init({\n config,\n logger,\n urlReader,\n httpRouter,\n discovery,\n database,\n }) {\n httpRouter.use(\n await createRouter({\n config,\n logger,\n urlReader,\n discovery,\n database,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","createRouter"],"mappings":";;;;;AA0BO,MAAM,qBAAqBA,oCAAoB,CAAA;AAAA,EACpD,QAAU,EAAA,eAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,UAAUA,6BAAa,CAAA;AAAA,OACzB;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,UAAA;AAAA,QACA,SAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAMC,mBAAa,CAAA;AAAA,YACjB,MAAA;AAAA,YACA,MAAA;AAAA,YACA,SAAA;AAAA,YACA,SAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AACA,QAAA,UAAA,CAAW,aAAc,CAAA;AAAA,UACvB,IAAM,EAAA,SAAA;AAAA,UACN,KAAO,EAAA;AAAA,SACR,CAAA;AAAA;AACH,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeCoverageDatabase.cjs.js","sources":["../../src/service/CodeCoverageDatabase.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PluginDatabaseManager,\n resolvePackagePath,\n} from '@backstage/backend-common';\nimport { NotFoundError } from '@backstage/errors';\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport { aggregateCoverage } from './CoverageUtils';\nimport { JsonCodeCoverage, JsonCoverageHistory } from './types';\n\nexport type RawDbCoverageRow = {\n id: string;\n entity: string;\n coverage: string;\n};\n\nexport interface CodeCoverageStore {\n insertCodeCoverage(\n coverage: JsonCodeCoverage,\n ): Promise<{ codeCoverageId: string }>;\n getCodeCoverage(entity: string): Promise<JsonCodeCoverage>;\n getHistory(entity: string, limit: number): Promise<JsonCoverageHistory>;\n}\n\nconst migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-code-coverage-backend',\n 'migrations',\n);\n\nexport class CodeCoverageDatabase implements CodeCoverageStore {\n static async create(\n database: PluginDatabaseManager,\n ): Promise<CodeCoverageStore> {\n const knex = await database.getClient();\n\n if (!database.migrations?.skip) {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new CodeCoverageDatabase(knex);\n }\n\n constructor(private readonly db: Knex) {}\n\n async insertCodeCoverage(\n coverage: JsonCodeCoverage,\n ): Promise<{ codeCoverageId: string }> {\n const codeCoverageId = uuid();\n const entity = stringifyEntityRef({\n kind: coverage.entity.kind,\n namespace: coverage.entity.namespace,\n name: coverage.entity.name,\n });\n\n await this.db<RawDbCoverageRow>('code_coverage').insert({\n id: codeCoverageId,\n entity: entity,\n coverage: JSON.stringify(coverage),\n });\n\n return { codeCoverageId };\n }\n\n async getCodeCoverage(entity: string): Promise<JsonCodeCoverage> {\n const [result] = await this.db<RawDbCoverageRow>('code_coverage')\n .where({ entity: entity })\n .orderBy('index', 'desc')\n .limit(1)\n .select();\n\n if (!result) {\n throw new NotFoundError(\n `No coverage for entity '${JSON.stringify(entity)}' found`,\n );\n }\n\n try {\n return JSON.parse(result.coverage);\n } catch (error) {\n throw new Error(`Failed to parse coverage for '${entity}', ${error}`);\n }\n }\n\n async getHistory(\n entity: string,\n limit: number,\n ): Promise<JsonCoverageHistory> {\n const res = await this.db<RawDbCoverageRow>('code_coverage')\n .where({ entity: entity })\n .orderBy('index', 'desc')\n .limit(limit)\n .select();\n\n const history = res\n .map(r => JSON.parse(r.coverage))\n .map(c => aggregateCoverage(c));\n\n const entityName = parseEntityRef(entity);\n\n return {\n entity: {\n name: entityName.name,\n kind: entityName.kind,\n namespace: entityName.namespace,\n },\n history: history,\n };\n }\n}\n"],"names":["resolvePackagePath","uuid","stringifyEntityRef","NotFoundError","aggregateCoverage","parseEntityRef"],"mappings":";;;;;;;;AAwCA,MAAM,aAAgB,GAAAA,gCAAA;AAAA,EACpB,mDAAA;AAAA,EACA
|
|
1
|
+
{"version":3,"file":"CodeCoverageDatabase.cjs.js","sources":["../../src/service/CodeCoverageDatabase.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PluginDatabaseManager,\n resolvePackagePath,\n} from '@backstage/backend-common';\nimport { NotFoundError } from '@backstage/errors';\nimport { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport { aggregateCoverage } from './CoverageUtils';\nimport { JsonCodeCoverage, JsonCoverageHistory } from './types';\n\nexport type RawDbCoverageRow = {\n id: string;\n entity: string;\n coverage: string;\n};\n\nexport interface CodeCoverageStore {\n insertCodeCoverage(\n coverage: JsonCodeCoverage,\n ): Promise<{ codeCoverageId: string }>;\n getCodeCoverage(entity: string): Promise<JsonCodeCoverage>;\n getHistory(entity: string, limit: number): Promise<JsonCoverageHistory>;\n}\n\nconst migrationsDir = resolvePackagePath(\n '@backstage-community/plugin-code-coverage-backend',\n 'migrations',\n);\n\nexport class CodeCoverageDatabase implements CodeCoverageStore {\n static async create(\n database: PluginDatabaseManager,\n ): Promise<CodeCoverageStore> {\n const knex = await database.getClient();\n\n if (!database.migrations?.skip) {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new CodeCoverageDatabase(knex);\n }\n\n constructor(private readonly db: Knex) {}\n\n async insertCodeCoverage(\n coverage: JsonCodeCoverage,\n ): Promise<{ codeCoverageId: string }> {\n const codeCoverageId = uuid();\n const entity = stringifyEntityRef({\n kind: coverage.entity.kind,\n namespace: coverage.entity.namespace,\n name: coverage.entity.name,\n });\n\n await this.db<RawDbCoverageRow>('code_coverage').insert({\n id: codeCoverageId,\n entity: entity,\n coverage: JSON.stringify(coverage),\n });\n\n return { codeCoverageId };\n }\n\n async getCodeCoverage(entity: string): Promise<JsonCodeCoverage> {\n const [result] = await this.db<RawDbCoverageRow>('code_coverage')\n .where({ entity: entity })\n .orderBy('index', 'desc')\n .limit(1)\n .select();\n\n if (!result) {\n throw new NotFoundError(\n `No coverage for entity '${JSON.stringify(entity)}' found`,\n );\n }\n\n try {\n return JSON.parse(result.coverage);\n } catch (error) {\n throw new Error(`Failed to parse coverage for '${entity}', ${error}`);\n }\n }\n\n async getHistory(\n entity: string,\n limit: number,\n ): Promise<JsonCoverageHistory> {\n const res = await this.db<RawDbCoverageRow>('code_coverage')\n .where({ entity: entity })\n .orderBy('index', 'desc')\n .limit(limit)\n .select();\n\n const history = res\n .map(r => JSON.parse(r.coverage))\n .map(c => aggregateCoverage(c));\n\n const entityName = parseEntityRef(entity);\n\n return {\n entity: {\n name: entityName.name,\n kind: entityName.kind,\n namespace: entityName.namespace,\n },\n history: history,\n };\n }\n}\n"],"names":["resolvePackagePath","uuid","stringifyEntityRef","NotFoundError","aggregateCoverage","parseEntityRef"],"mappings":";;;;;;;;AAwCA,MAAM,aAAgB,GAAAA,gCAAA;AAAA,EACpB,mDAAA;AAAA,EACA;AACF,CAAA;AAEO,MAAM,oBAAkD,CAAA;AAAA,EAe7D,YAA6B,EAAU,EAAA;AAAV,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAA;AAAW,EAdxC,aAAa,OACX,QAC4B,EAAA;AAC5B,IAAM,MAAA,IAAA,GAAO,MAAM,QAAA,CAAS,SAAU,EAAA;AAEtC,IAAI,IAAA,CAAC,QAAS,CAAA,UAAA,EAAY,IAAM,EAAA;AAC9B,MAAM,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA;AAAA,QACxB,SAAW,EAAA;AAAA,OACZ,CAAA;AAAA;AAGH,IAAO,OAAA,IAAI,qBAAqB,IAAI,CAAA;AAAA;AACtC,EAIA,MAAM,mBACJ,QACqC,EAAA;AACrC,IAAA,MAAM,iBAAiBC,OAAK,EAAA;AAC5B,IAAA,MAAM,SAASC,+BAAmB,CAAA;AAAA,MAChC,IAAA,EAAM,SAAS,MAAO,CAAA,IAAA;AAAA,MACtB,SAAA,EAAW,SAAS,MAAO,CAAA,SAAA;AAAA,MAC3B,IAAA,EAAM,SAAS,MAAO,CAAA;AAAA,KACvB,CAAA;AAED,IAAA,MAAM,IAAK,CAAA,EAAA,CAAqB,eAAe,CAAA,CAAE,MAAO,CAAA;AAAA,MACtD,EAAI,EAAA,cAAA;AAAA,MACJ,MAAA;AAAA,MACA,QAAA,EAAU,IAAK,CAAA,SAAA,CAAU,QAAQ;AAAA,KAClC,CAAA;AAED,IAAA,OAAO,EAAE,cAAe,EAAA;AAAA;AAC1B,EAEA,MAAM,gBAAgB,MAA2C,EAAA;AAC/D,IAAM,MAAA,CAAC,MAAM,CAAI,GAAA,MAAM,KAAK,EAAqB,CAAA,eAAe,EAC7D,KAAM,CAAA,EAAE,QAAgB,CAAA,CACxB,QAAQ,OAAS,EAAA,MAAM,EACvB,KAAM,CAAA,CAAC,EACP,MAAO,EAAA;AAEV,IAAA,IAAI,CAAC,MAAQ,EAAA;AACX,MAAA,MAAM,IAAIC,oBAAA;AAAA,QACR,CAA2B,wBAAA,EAAA,IAAA,CAAK,SAAU,CAAA,MAAM,CAAC,CAAA,OAAA;AAAA,OACnD;AAAA;AAGF,IAAI,IAAA;AACF,MAAO,OAAA,IAAA,CAAK,KAAM,CAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,aAC1B,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,8BAAA,EAAiC,MAAM,CAAA,GAAA,EAAM,KAAK,CAAE,CAAA,CAAA;AAAA;AACtE;AACF,EAEA,MAAM,UACJ,CAAA,MAAA,EACA,KAC8B,EAAA;AAC9B,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,GAAqB,eAAe,CAAA,CACxD,MAAM,EAAE,MAAA,EAAgB,CAAA,CACxB,QAAQ,OAAS,EAAA,MAAM,EACvB,KAAM,CAAA,KAAK,EACX,MAAO,EAAA;AAEV,IAAA,MAAM,OAAU,GAAA,GAAA,CACb,GAAI,CAAA,CAAA,CAAA,KAAK,KAAK,KAAM,CAAA,CAAA,CAAE,QAAQ,CAAC,CAC/B,CAAA,GAAA,CAAI,CAAK,CAAA,KAAAC,+BAAA,CAAkB,CAAC,CAAC,CAAA;AAEhC,IAAM,MAAA,UAAA,GAAaC,4BAAe,MAAM,CAAA;AAExC,IAAO,OAAA;AAAA,MACL,MAAQ,EAAA;AAAA,QACN,MAAM,UAAW,CAAA,IAAA;AAAA,QACjB,MAAM,UAAW,CAAA,IAAA;AAAA,QACjB,WAAW,UAAW,CAAA;AAAA,OACxB;AAAA,MACA;AAAA,KACF;AAAA;AAEJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoverageUtils.cjs.js","sources":["../../src/service/CoverageUtils.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Request } from 'express';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport {\n Entity,\n getEntitySourceLocation,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { ScmIntegration, ScmIntegrations } from '@backstage/integration';\nimport { AggregateCoverage, FileEntry, JsonCodeCoverage } from './types';\nimport { UrlReaderService } from '@backstage/backend-plugin-api';\n\nexport const calculatePercentage = (\n available: number,\n covered: number,\n): number => {\n if (available === 0) {\n return 0;\n }\n return parseFloat(((covered / available) * 100).toFixed(2));\n};\n\nexport const aggregateCoverage = (c: JsonCodeCoverage): AggregateCoverage => {\n let availableLine = 0;\n let coveredLine = 0;\n let availableBranch = 0;\n let coveredBranch = 0;\n c.files.forEach(f => {\n availableLine += Object.keys(f.lineHits).length;\n coveredLine += Object.values(f.lineHits).filter(l => l > 0).length;\n\n availableBranch += Object.keys(f.branchHits)\n .map(b => parseInt(b, 10))\n .map((b: number) => f.branchHits[b].available)\n .filter(Boolean)\n .reduce((acc, curr) => acc + curr, 0);\n coveredBranch += Object.keys(f.branchHits)\n .map(b => parseInt(b, 10))\n .map((b: number) => f.branchHits[b].covered)\n .filter(Boolean)\n .reduce((acc, curr) => acc + curr, 0);\n });\n\n return {\n timestamp: c.metadata.generationTime,\n branch: {\n available: availableBranch,\n covered: coveredBranch,\n missed: availableBranch - coveredBranch,\n percentage: calculatePercentage(availableBranch, coveredBranch),\n },\n line: {\n available: availableLine,\n covered: coveredLine,\n missed: availableLine - coveredLine,\n percentage: calculatePercentage(availableLine, coveredLine),\n },\n };\n};\n\nexport class CoverageUtils {\n constructor(\n readonly scm: Partial<ScmIntegrations>,\n readonly urlReader: Partial<UrlReaderService>,\n ) {}\n\n async processCoveragePayload(\n entity: Entity,\n req: Request,\n ): Promise<{\n sourceLocation?: string;\n vcs?: ScmIntegration;\n scmFiles: string[];\n body: {};\n }> {\n const enforceScmFiles =\n entity.metadata.annotations?.['backstage.io/code-coverage'] ===\n 'scm-only' || false;\n\n let sourceLocation: string | undefined = undefined;\n let vcs: ScmIntegration | undefined = undefined;\n let scmFiles: string[] = [];\n\n if (enforceScmFiles) {\n try {\n const sl = getEntitySourceLocation(entity);\n sourceLocation = sl.target;\n } catch (e: unknown) {\n // TODO: logging\n }\n\n if (!sourceLocation) {\n throw new InputError(\n `No \"backstage.io/source-location\" annotation on entity ${stringifyEntityRef(\n entity,\n )}`,\n );\n }\n\n vcs = this.scm.byUrl?.(sourceLocation);\n if (!vcs) {\n throw new InputError(`Unable to determine SCM from ${sourceLocation}`);\n }\n\n const scmTree = await this.urlReader.readTree?.(sourceLocation);\n if (!scmTree) {\n throw new NotFoundError(`Unable to read tree from ${sourceLocation}`);\n }\n scmFiles = (await scmTree.files()).map(f => f.path);\n }\n\n const body = this.validateRequestBody(req);\n if (Object.keys(body).length === 0) {\n throw new InputError('Unable to parse body');\n }\n\n return {\n sourceLocation,\n vcs,\n scmFiles,\n body,\n };\n }\n\n async buildCoverage(\n entity: Entity,\n sourceLocation: string | undefined,\n vcs: ScmIntegration | undefined,\n files: FileEntry[],\n ): Promise<JsonCodeCoverage> {\n return {\n metadata: {\n vcs: {\n type: vcs?.type || 'unknown',\n location: sourceLocation || 'unknown',\n },\n generationTime: Date.now(),\n },\n entity: {\n name: entity.metadata.name,\n namespace: entity.metadata.namespace || 'default',\n kind: entity.kind,\n },\n files,\n };\n }\n\n validateRequestBody(req: Request) {\n const contentType = req.headers['content-type'];\n if (!contentType) {\n throw new InputError('Content-Type header missing');\n // text/xml or text/plain is allowed\n } else if (!contentType.match(/^text\\/xml|plain($|;)/)) {\n throw new InputError(\n `Content-Type header \"${contentType}\" not supported, expected \"text/xml\" or \"text/plain\" possibly followed by a charset`,\n );\n }\n const body = req.body;\n if (!body) {\n throw new InputError('Missing request body');\n }\n return body;\n }\n}\n"],"names":["getEntitySourceLocation","InputError","stringifyEntityRef","NotFoundError"],"mappings":";;;;;AA0Ba,MAAA,mBAAA,GAAsB,CACjC,SAAA,EACA,OACW,KAAA;AACX,EAAA,IAAI,cAAc,CAAG,EAAA;AACnB,IAAO,OAAA,CAAA
|
|
1
|
+
{"version":3,"file":"CoverageUtils.cjs.js","sources":["../../src/service/CoverageUtils.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Request } from 'express';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport {\n Entity,\n getEntitySourceLocation,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { ScmIntegration, ScmIntegrations } from '@backstage/integration';\nimport { AggregateCoverage, FileEntry, JsonCodeCoverage } from './types';\nimport { UrlReaderService } from '@backstage/backend-plugin-api';\n\nexport const calculatePercentage = (\n available: number,\n covered: number,\n): number => {\n if (available === 0) {\n return 0;\n }\n return parseFloat(((covered / available) * 100).toFixed(2));\n};\n\nexport const aggregateCoverage = (c: JsonCodeCoverage): AggregateCoverage => {\n let availableLine = 0;\n let coveredLine = 0;\n let availableBranch = 0;\n let coveredBranch = 0;\n c.files.forEach(f => {\n availableLine += Object.keys(f.lineHits).length;\n coveredLine += Object.values(f.lineHits).filter(l => l > 0).length;\n\n availableBranch += Object.keys(f.branchHits)\n .map(b => parseInt(b, 10))\n .map((b: number) => f.branchHits[b].available)\n .filter(Boolean)\n .reduce((acc, curr) => acc + curr, 0);\n coveredBranch += Object.keys(f.branchHits)\n .map(b => parseInt(b, 10))\n .map((b: number) => f.branchHits[b].covered)\n .filter(Boolean)\n .reduce((acc, curr) => acc + curr, 0);\n });\n\n return {\n timestamp: c.metadata.generationTime,\n branch: {\n available: availableBranch,\n covered: coveredBranch,\n missed: availableBranch - coveredBranch,\n percentage: calculatePercentage(availableBranch, coveredBranch),\n },\n line: {\n available: availableLine,\n covered: coveredLine,\n missed: availableLine - coveredLine,\n percentage: calculatePercentage(availableLine, coveredLine),\n },\n };\n};\n\nexport class CoverageUtils {\n constructor(\n readonly scm: Partial<ScmIntegrations>,\n readonly urlReader: Partial<UrlReaderService>,\n ) {}\n\n async processCoveragePayload(\n entity: Entity,\n req: Request,\n ): Promise<{\n sourceLocation?: string;\n vcs?: ScmIntegration;\n scmFiles: string[];\n body: {};\n }> {\n const enforceScmFiles =\n entity.metadata.annotations?.['backstage.io/code-coverage'] ===\n 'scm-only' || false;\n\n let sourceLocation: string | undefined = undefined;\n let vcs: ScmIntegration | undefined = undefined;\n let scmFiles: string[] = [];\n\n if (enforceScmFiles) {\n try {\n const sl = getEntitySourceLocation(entity);\n sourceLocation = sl.target;\n } catch (e: unknown) {\n // TODO: logging\n }\n\n if (!sourceLocation) {\n throw new InputError(\n `No \"backstage.io/source-location\" annotation on entity ${stringifyEntityRef(\n entity,\n )}`,\n );\n }\n\n vcs = this.scm.byUrl?.(sourceLocation);\n if (!vcs) {\n throw new InputError(`Unable to determine SCM from ${sourceLocation}`);\n }\n\n const scmTree = await this.urlReader.readTree?.(sourceLocation);\n if (!scmTree) {\n throw new NotFoundError(`Unable to read tree from ${sourceLocation}`);\n }\n scmFiles = (await scmTree.files()).map(f => f.path);\n }\n\n const body = this.validateRequestBody(req);\n if (Object.keys(body).length === 0) {\n throw new InputError('Unable to parse body');\n }\n\n return {\n sourceLocation,\n vcs,\n scmFiles,\n body,\n };\n }\n\n async buildCoverage(\n entity: Entity,\n sourceLocation: string | undefined,\n vcs: ScmIntegration | undefined,\n files: FileEntry[],\n ): Promise<JsonCodeCoverage> {\n return {\n metadata: {\n vcs: {\n type: vcs?.type || 'unknown',\n location: sourceLocation || 'unknown',\n },\n generationTime: Date.now(),\n },\n entity: {\n name: entity.metadata.name,\n namespace: entity.metadata.namespace || 'default',\n kind: entity.kind,\n },\n files,\n };\n }\n\n validateRequestBody(req: Request) {\n const contentType = req.headers['content-type'];\n if (!contentType) {\n throw new InputError('Content-Type header missing');\n // text/xml or text/plain is allowed\n } else if (!contentType.match(/^text\\/xml|plain($|;)/)) {\n throw new InputError(\n `Content-Type header \"${contentType}\" not supported, expected \"text/xml\" or \"text/plain\" possibly followed by a charset`,\n );\n }\n const body = req.body;\n if (!body) {\n throw new InputError('Missing request body');\n }\n return body;\n }\n}\n"],"names":["getEntitySourceLocation","InputError","stringifyEntityRef","NotFoundError"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cobertura.cjs.js","sources":["../../../src/service/converter/cobertura.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { BranchHit, FileEntry } from '../types';\nimport { CoberturaXML, InnerClass, LineHit } from './types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class Cobertura implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * convert cobertura into shared json coverage format\n *\n * @param xml - cobertura xml object\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(xml: CoberturaXML, scmFiles: string[]): FileEntry[] {\n const ppc = xml.coverage.packages\n ?.flatMap(p => p.package)\n .filter(Boolean)\n .flatMap(p => p.classes);\n const pc = xml.coverage.package?.filter(Boolean).flatMap(p => p.classes);\n\n const classes = [ppc, pc]\n .flat()\n .filter(Boolean)\n .flatMap(c => c.class)\n .filter(Boolean);\n const jscov: Array<FileEntry> = [];\n\n classes.forEach(c => {\n const packageAndFilename = c.$.filename;\n const lines = this.extractLines(c);\n const lineHits: Record<number, number> = {};\n const branchHits: Record<number, BranchHit> = {};\n\n lines.forEach(l => {\n if (!lineHits[l.number]) {\n lineHits[l.number] = 0;\n }\n lineHits[l.number] += l.hits;\n if (l.branch && l['condition-coverage']) {\n const bh = this.parseBranch(l['condition-coverage']);\n if (bh) {\n branchHits[l.number] = bh;\n }\n }\n });\n\n const currentFile = scmFiles\n .map(f => f.trimEnd())\n .find(f => f.endsWith(packageAndFilename));\n this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);\n if (\n scmFiles.length === 0 ||\n (Object.keys(lineHits).length > 0 && currentFile)\n ) {\n jscov.push({\n filename: currentFile || packageAndFilename,\n branchHits: branchHits,\n lineHits: lineHits,\n });\n }\n });\n\n return jscov;\n }\n\n /**\n * Parses branch coverage information from condition-coverage\n *\n * @param condition - condition-coverage value from line coverage\n */\n private parseBranch(condition: string): BranchHit | null {\n const pattern = /[0-9\\.]+\\%\\s\\(([0-9]+)\\/([0-9]+)\\)/;\n const match = condition.match(pattern);\n if (!match) {\n return null;\n }\n const covered = parseInt(match[1], 10);\n const available = parseInt(match[2], 10);\n return {\n covered: covered,\n missed: available - covered,\n available: available,\n };\n }\n\n /**\n * Extract line hits from a class coverage entry\n *\n * @param clz - class coverage information\n */\n private extractLines(clz: InnerClass): Array<LineHit> {\n const classLines = clz.lines.flatMap(l => l.line);\n const methodLines = clz.methods\n ?.flatMap(m => m.method)\n .filter(Boolean)\n .flatMap(m => m.lines)\n .filter(Boolean)\n .flatMap(l => l.line)\n .filter(\n ({ $: methodLine }) =>\n classLines.some(\n ({ $: classLine }) => methodLine.number === classLine.number,\n ) === false,\n );\n const lines = [classLines, methodLines].flat().filter(Boolean);\n const lineHits = lines.map(l => {\n return {\n number: parseInt(l.$.number as unknown as string, 10),\n hits: parseInt(l.$.hits as unknown as string, 10),\n 'condition-coverage': l.$['condition-coverage'],\n branch: l.$.branch,\n };\n });\n return lineHits;\n }\n}\n"],"names":[],"mappings":";;AAoBO,MAAM,SAA+B,CAAA;AAAA,EAC1C,YAAqB,MAAuB,EAAA;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA
|
|
1
|
+
{"version":3,"file":"cobertura.cjs.js","sources":["../../../src/service/converter/cobertura.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { BranchHit, FileEntry } from '../types';\nimport { CoberturaXML, InnerClass, LineHit } from './types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class Cobertura implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * convert cobertura into shared json coverage format\n *\n * @param xml - cobertura xml object\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(xml: CoberturaXML, scmFiles: string[]): FileEntry[] {\n const ppc = xml.coverage.packages\n ?.flatMap(p => p.package)\n .filter(Boolean)\n .flatMap(p => p.classes);\n const pc = xml.coverage.package?.filter(Boolean).flatMap(p => p.classes);\n\n const classes = [ppc, pc]\n .flat()\n .filter(Boolean)\n .flatMap(c => c.class)\n .filter(Boolean);\n const jscov: Array<FileEntry> = [];\n\n classes.forEach(c => {\n const packageAndFilename = c.$.filename;\n const lines = this.extractLines(c);\n const lineHits: Record<number, number> = {};\n const branchHits: Record<number, BranchHit> = {};\n\n lines.forEach(l => {\n if (!lineHits[l.number]) {\n lineHits[l.number] = 0;\n }\n lineHits[l.number] += l.hits;\n if (l.branch && l['condition-coverage']) {\n const bh = this.parseBranch(l['condition-coverage']);\n if (bh) {\n branchHits[l.number] = bh;\n }\n }\n });\n\n const currentFile = scmFiles\n .map(f => f.trimEnd())\n .find(f => f.endsWith(packageAndFilename));\n this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);\n if (\n scmFiles.length === 0 ||\n (Object.keys(lineHits).length > 0 && currentFile)\n ) {\n jscov.push({\n filename: currentFile || packageAndFilename,\n branchHits: branchHits,\n lineHits: lineHits,\n });\n }\n });\n\n return jscov;\n }\n\n /**\n * Parses branch coverage information from condition-coverage\n *\n * @param condition - condition-coverage value from line coverage\n */\n private parseBranch(condition: string): BranchHit | null {\n const pattern = /[0-9\\.]+\\%\\s\\(([0-9]+)\\/([0-9]+)\\)/;\n const match = condition.match(pattern);\n if (!match) {\n return null;\n }\n const covered = parseInt(match[1], 10);\n const available = parseInt(match[2], 10);\n return {\n covered: covered,\n missed: available - covered,\n available: available,\n };\n }\n\n /**\n * Extract line hits from a class coverage entry\n *\n * @param clz - class coverage information\n */\n private extractLines(clz: InnerClass): Array<LineHit> {\n const classLines = clz.lines.flatMap(l => l.line);\n const methodLines = clz.methods\n ?.flatMap(m => m.method)\n .filter(Boolean)\n .flatMap(m => m.lines)\n .filter(Boolean)\n .flatMap(l => l.line)\n .filter(\n ({ $: methodLine }) =>\n classLines.some(\n ({ $: classLine }) => methodLine.number === classLine.number,\n ) === false,\n );\n const lines = [classLines, methodLines].flat().filter(Boolean);\n const lineHits = lines.map(l => {\n return {\n number: parseInt(l.$.number as unknown as string, 10),\n hits: parseInt(l.$.hits as unknown as string, 10),\n 'condition-coverage': l.$['condition-coverage'],\n branch: l.$.branch,\n };\n });\n return lineHits;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jacoco.cjs.js","sources":["../../../src/service/converter/jacoco.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BranchHit, FileEntry } from '../types';\nimport { JacocoSourceFile, JacocoXML } from './types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\ntype ParsedLine = {\n number: number;\n missed_instructions: number;\n covered_instructions: number;\n missed_branches: number;\n covered_branches: number;\n};\n\nexport class Jacoco implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * Converts jacoco into shared json coverage format\n *\n * @param xml - jacoco xml object\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(xml: JacocoXML, scmFiles: Array<string>): Array<FileEntry> {\n const jscov: Array<FileEntry> = [];\n xml.report.package.forEach(r => {\n const packageName = r.$.name;\n r.sourcefile.forEach(sf => {\n const fileName = sf.$.name;\n const lines = this.extractLines(sf);\n const lineHits: Record<number, number> = {};\n const branchHits: Record<number, BranchHit> = {};\n lines.forEach(l => {\n if (!lineHits[l.number]) {\n lineHits[l.number] = 0;\n }\n lineHits[l.number] += l.covered_instructions;\n const ab = l.covered_branches + l.missed_branches;\n if (ab > 0) {\n branchHits[l.number] = {\n covered: l.covered_branches,\n missed: l.missed_branches,\n available: ab,\n };\n }\n });\n\n const packageAndFilename = `${packageName}/${fileName}`;\n const currentFile = scmFiles\n .map(f => f.trimEnd())\n .find(f => f.endsWith(packageAndFilename));\n this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);\n if (\n scmFiles.length === 0 ||\n (Object.keys(lineHits).length > 0 && currentFile)\n ) {\n jscov.push({\n filename: currentFile || packageAndFilename,\n branchHits: branchHits,\n lineHits: lineHits,\n });\n }\n });\n });\n\n return jscov;\n }\n\n private extractLines(sourcefile: JacocoSourceFile): ParsedLine[] {\n const parsed: ParsedLine[] = [];\n\n sourcefile.line?.forEach(l => {\n parsed.push({\n number: parseInt(l.$.nr, 10),\n missed_instructions: parseInt(l.$.mi, 10),\n covered_instructions: parseInt(l.$.ci, 10),\n missed_branches: parseInt(l.$.mb, 10),\n covered_branches: parseInt(l.$.cb, 10),\n });\n });\n\n return parsed;\n }\n}\n"],"names":[],"mappings":";;AA6BO,MAAM,MAA4B,CAAA;AAAA,EACvC,YAAqB,MAAuB,EAAA;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA
|
|
1
|
+
{"version":3,"file":"jacoco.cjs.js","sources":["../../../src/service/converter/jacoco.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { BranchHit, FileEntry } from '../types';\nimport { JacocoSourceFile, JacocoXML } from './types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\ntype ParsedLine = {\n number: number;\n missed_instructions: number;\n covered_instructions: number;\n missed_branches: number;\n covered_branches: number;\n};\n\nexport class Jacoco implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * Converts jacoco into shared json coverage format\n *\n * @param xml - jacoco xml object\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(xml: JacocoXML, scmFiles: Array<string>): Array<FileEntry> {\n const jscov: Array<FileEntry> = [];\n xml.report.package.forEach(r => {\n const packageName = r.$.name;\n r.sourcefile.forEach(sf => {\n const fileName = sf.$.name;\n const lines = this.extractLines(sf);\n const lineHits: Record<number, number> = {};\n const branchHits: Record<number, BranchHit> = {};\n lines.forEach(l => {\n if (!lineHits[l.number]) {\n lineHits[l.number] = 0;\n }\n lineHits[l.number] += l.covered_instructions;\n const ab = l.covered_branches + l.missed_branches;\n if (ab > 0) {\n branchHits[l.number] = {\n covered: l.covered_branches,\n missed: l.missed_branches,\n available: ab,\n };\n }\n });\n\n const packageAndFilename = `${packageName}/${fileName}`;\n const currentFile = scmFiles\n .map(f => f.trimEnd())\n .find(f => f.endsWith(packageAndFilename));\n this.logger.debug(`matched ${packageAndFilename} to ${currentFile}`);\n if (\n scmFiles.length === 0 ||\n (Object.keys(lineHits).length > 0 && currentFile)\n ) {\n jscov.push({\n filename: currentFile || packageAndFilename,\n branchHits: branchHits,\n lineHits: lineHits,\n });\n }\n });\n });\n\n return jscov;\n }\n\n private extractLines(sourcefile: JacocoSourceFile): ParsedLine[] {\n const parsed: ParsedLine[] = [];\n\n sourcefile.line?.forEach(l => {\n parsed.push({\n number: parseInt(l.$.nr, 10),\n missed_instructions: parseInt(l.$.mi, 10),\n covered_instructions: parseInt(l.$.ci, 10),\n missed_branches: parseInt(l.$.mb, 10),\n covered_branches: parseInt(l.$.cb, 10),\n });\n });\n\n return parsed;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lcov.cjs.js","sources":["../../../src/service/converter/lcov.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { FileEntry } from '../types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class Lcov implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * convert lcov into shared json coverage format\n *\n * @param raw - raw lcov report\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(raw: string, scmFiles: string[]): FileEntry[] {\n const lines = raw.split(/\\r?\\n/);\n const jscov: Array<FileEntry> = [];\n let currentFile: FileEntry | null = null;\n\n lines.forEach(line => {\n const [section, value] = line.split(':');\n\n switch (section) {\n // If the line starts with SF, it's a new file\n case 'SF':\n currentFile = this.processNewFile(value, scmFiles);\n break;\n // If the line starts with DA, it's a line hit\n case 'DA':\n this.processLineHit(currentFile, value);\n break;\n // If the line starts with BRDA, it's a branch line\n case 'BRDA':\n this.processBranchHit(currentFile, value);\n break;\n // If the line starts with end_of_record, it's the end of current file\n case 'end_of_record':\n if (currentFile) {\n jscov.push(currentFile);\n currentFile = null;\n }\n break;\n default:\n break;\n }\n });\n\n return jscov;\n }\n\n /**\n * Parses a new file entry\n *\n * @param file - file name from coverage report\n * @param scmFiles - list of files that are committed to SCM\n */\n private processNewFile(file: string, scmFiles: string[]): FileEntry | null {\n const filename = scmFiles.map(f => f.trimEnd()).find(f => file.endsWith(f));\n\n this.logger.debug(`matched ${file} to ${filename}`);\n\n if (scmFiles.length === 0 || filename) {\n return {\n filename: filename || file,\n lineHits: {},\n branchHits: {},\n };\n }\n\n return null;\n }\n\n /**\n * Parses line coverage information\n *\n * @param currentFile - current file entry\n * @param value - line coverage information\n */\n private processLineHit(currentFile: FileEntry | null, value: string): void {\n if (!currentFile) return;\n\n const [lineNumber, hits] = value.split(',');\n currentFile.lineHits[Number(lineNumber)] = Number(hits);\n }\n\n /**\n * Parses branch coverage information\n *\n * @param currentFile - current file entry\n * @param value - branch coverage information\n */\n private processBranchHit(currentFile: FileEntry | null, value: string): void {\n if (!currentFile) return;\n\n const [lineNumber, , , hits] = value.split(',');\n const lineNumberNum = Number(lineNumber);\n const isHit = Number(hits) > 0;\n const branch = currentFile.branchHits[lineNumberNum] || {\n covered: 0,\n available: 0,\n missed: 0,\n };\n\n branch.available++;\n branch.covered += isHit ? 1 : 0;\n branch.missed += isHit ? 0 : 1;\n\n currentFile.branchHits[lineNumberNum] = branch;\n }\n}\n"],"names":[],"mappings":";;AAmBO,MAAM,IAA0B,CAAA;AAAA,EACrC,YAAqB,MAAuB,EAAA;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA
|
|
1
|
+
{"version":3,"file":"lcov.cjs.js","sources":["../../../src/service/converter/lcov.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { FileEntry } from '../types';\nimport { Converter } from './Converter';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nexport class Lcov implements Converter {\n constructor(readonly logger: LoggerService) {\n this.logger = logger;\n }\n\n /**\n * convert lcov into shared json coverage format\n *\n * @param raw - raw lcov report\n * @param scmFiles - list of files that are committed to SCM\n */\n convert(raw: string, scmFiles: string[]): FileEntry[] {\n const lines = raw.split(/\\r?\\n/);\n const jscov: Array<FileEntry> = [];\n let currentFile: FileEntry | null = null;\n\n lines.forEach(line => {\n const [section, value] = line.split(':');\n\n switch (section) {\n // If the line starts with SF, it's a new file\n case 'SF':\n currentFile = this.processNewFile(value, scmFiles);\n break;\n // If the line starts with DA, it's a line hit\n case 'DA':\n this.processLineHit(currentFile, value);\n break;\n // If the line starts with BRDA, it's a branch line\n case 'BRDA':\n this.processBranchHit(currentFile, value);\n break;\n // If the line starts with end_of_record, it's the end of current file\n case 'end_of_record':\n if (currentFile) {\n jscov.push(currentFile);\n currentFile = null;\n }\n break;\n default:\n break;\n }\n });\n\n return jscov;\n }\n\n /**\n * Parses a new file entry\n *\n * @param file - file name from coverage report\n * @param scmFiles - list of files that are committed to SCM\n */\n private processNewFile(file: string, scmFiles: string[]): FileEntry | null {\n const filename = scmFiles.map(f => f.trimEnd()).find(f => file.endsWith(f));\n\n this.logger.debug(`matched ${file} to ${filename}`);\n\n if (scmFiles.length === 0 || filename) {\n return {\n filename: filename || file,\n lineHits: {},\n branchHits: {},\n };\n }\n\n return null;\n }\n\n /**\n * Parses line coverage information\n *\n * @param currentFile - current file entry\n * @param value - line coverage information\n */\n private processLineHit(currentFile: FileEntry | null, value: string): void {\n if (!currentFile) return;\n\n const [lineNumber, hits] = value.split(',');\n currentFile.lineHits[Number(lineNumber)] = Number(hits);\n }\n\n /**\n * Parses branch coverage information\n *\n * @param currentFile - current file entry\n * @param value - branch coverage information\n */\n private processBranchHit(currentFile: FileEntry | null, value: string): void {\n if (!currentFile) return;\n\n const [lineNumber, , , hits] = value.split(',');\n const lineNumberNum = Number(lineNumber);\n const isHit = Number(hits) > 0;\n const branch = currentFile.branchHits[lineNumberNum] || {\n covered: 0,\n available: 0,\n missed: 0,\n };\n\n branch.available++;\n branch.covered += isHit ? 1 : 0;\n branch.missed += isHit ? 0 : 1;\n\n currentFile.branchHits[lineNumberNum] = branch;\n }\n}\n"],"names":[],"mappings":";;AAmBO,MAAM,IAA0B,CAAA;AAAA,EACrC,YAAqB,MAAuB,EAAA;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACnB,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA;AAAA;AAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAA,CAAQ,KAAa,QAAiC,EAAA;AACpD,IAAM,MAAA,KAAA,GAAQ,GAAI,CAAA,KAAA,CAAM,OAAO,CAAA;AAC/B,IAAA,MAAM,QAA0B,EAAC;AACjC,IAAA,IAAI,WAAgC,GAAA,IAAA;AAEpC,IAAA,KAAA,CAAM,QAAQ,CAAQ,IAAA,KAAA;AACpB,MAAA,MAAM,CAAC,OAAS,EAAA,KAAK,CAAI,GAAA,IAAA,CAAK,MAAM,GAAG,CAAA;AAEvC,MAAA,QAAQ,OAAS;AAAA;AAAA,QAEf,KAAK,IAAA;AACH,UAAc,WAAA,GAAA,IAAA,CAAK,cAAe,CAAA,KAAA,EAAO,QAAQ,CAAA;AACjD,UAAA;AAAA;AAAA,QAEF,KAAK,IAAA;AACH,UAAK,IAAA,CAAA,cAAA,CAAe,aAAa,KAAK,CAAA;AACtC,UAAA;AAAA;AAAA,QAEF,KAAK,MAAA;AACH,UAAK,IAAA,CAAA,gBAAA,CAAiB,aAAa,KAAK,CAAA;AACxC,UAAA;AAAA;AAAA,QAEF,KAAK,eAAA;AACH,UAAA,IAAI,WAAa,EAAA;AACf,YAAA,KAAA,CAAM,KAAK,WAAW,CAAA;AACtB,YAAc,WAAA,GAAA,IAAA;AAAA;AAEhB,UAAA;AAEA;AACJ,KACD,CAAA;AAED,IAAO,OAAA,KAAA;AAAA;AACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAA,CAAe,MAAc,QAAsC,EAAA;AACzE,IAAA,MAAM,QAAW,GAAA,QAAA,CAAS,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,OAAA,EAAS,CAAA,CAAE,IAAK,CAAA,CAAA,CAAA,KAAK,IAAK,CAAA,QAAA,CAAS,CAAC,CAAC,CAAA;AAE1E,IAAA,IAAA,CAAK,OAAO,KAAM,CAAA,CAAA,QAAA,EAAW,IAAI,CAAA,IAAA,EAAO,QAAQ,CAAE,CAAA,CAAA;AAElD,IAAI,IAAA,QAAA,CAAS,MAAW,KAAA,CAAA,IAAK,QAAU,EAAA;AACrC,MAAO,OAAA;AAAA,QACL,UAAU,QAAY,IAAA,IAAA;AAAA,QACtB,UAAU,EAAC;AAAA,QACX,YAAY;AAAC,OACf;AAAA;AAGF,IAAO,OAAA,IAAA;AAAA;AACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAA,CAAe,aAA+B,KAAqB,EAAA;AACzE,IAAA,IAAI,CAAC,WAAa,EAAA;AAElB,IAAA,MAAM,CAAC,UAAY,EAAA,IAAI,CAAI,GAAA,KAAA,CAAM,MAAM,GAAG,CAAA;AAC1C,IAAA,WAAA,CAAY,SAAS,MAAO,CAAA,UAAU,CAAC,CAAA,GAAI,OAAO,IAAI,CAAA;AAAA;AACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAA,CAAiB,aAA+B,KAAqB,EAAA;AAC3E,IAAA,IAAI,CAAC,WAAa,EAAA;AAElB,IAAM,MAAA,CAAC,gBAAgB,IAAI,CAAI,GAAA,KAAA,CAAM,MAAM,GAAG,CAAA;AAC9C,IAAM,MAAA,aAAA,GAAgB,OAAO,UAAU,CAAA;AACvC,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,IAAI,CAAI,GAAA,CAAA;AAC7B,IAAA,MAAM,MAAS,GAAA,WAAA,CAAY,UAAW,CAAA,aAAa,CAAK,IAAA;AAAA,MACtD,OAAS,EAAA,CAAA;AAAA,MACT,SAAW,EAAA,CAAA;AAAA,MACX,MAAQ,EAAA;AAAA,KACV;AAEA,IAAO,MAAA,CAAA,SAAA,EAAA;AACP,IAAO,MAAA,CAAA,OAAA,IAAW,QAAQ,CAAI,GAAA,CAAA;AAC9B,IAAO,MAAA,CAAA,MAAA,IAAU,QAAQ,CAAI,GAAA,CAAA;AAE7B,IAAY,WAAA,CAAA,UAAA,CAAW,aAAa,CAAI,GAAA,MAAA;AAAA;AAE5C;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport BodyParser from 'body-parser';\nimport bodyParserXml from 'body-parser-xml';\nimport { CatalogApi, CatalogClient } from '@backstage/catalog-client';\nimport { createLegacyAuthAdapters } from '@backstage/backend-common';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { CodeCoverageDatabase } from './CodeCoverageDatabase';\nimport { aggregateCoverage, CoverageUtils } from './CoverageUtils';\nimport { Cobertura, Converter, Jacoco, Lcov } from './converter';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport {\n AuthService,\n DatabaseService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n UrlReaderService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\n\n/**\n * Options for {@link createRouter}.\n *\n * @public\n */\nexport interface RouterOptions {\n config: Config;\n discovery: DiscoveryService;\n database: DatabaseService;\n urlReader: UrlReaderService;\n logger: LoggerService;\n catalogApi?: CatalogApi;\n auth?: AuthService;\n httpAuth?: HttpAuthService;\n}\n\nexport interface CodeCoverageApi {\n name: string;\n}\n\nexport const makeRouter = async (\n options: RouterOptions,\n): Promise<express.Router> => {\n const { config, logger, discovery, database, urlReader } = options;\n\n const codeCoverageDatabase = await CodeCoverageDatabase.create(database);\n const codecovUrl = await discovery.getExternalBaseUrl('code-coverage');\n const catalogApi =\n options.catalogApi ?? new CatalogClient({ discoveryApi: discovery });\n const scm = ScmIntegrations.fromConfig(config);\n const { auth, httpAuth } = createLegacyAuthAdapters(options);\n\n const bodySizeLimit =\n config.getOptionalString('codeCoverage.bodySizeLimit') ?? '100kb';\n\n bodyParserXml(BodyParser);\n const router = Router();\n router.use(\n BodyParser.xml({\n limit: bodySizeLimit,\n }),\n );\n router.use(\n BodyParser.text({\n limit: bodySizeLimit,\n }),\n );\n router.use(express.json());\n\n const utils = new CoverageUtils(scm, urlReader);\n\n router.get('/health', async (_req, res) => {\n res.status(200).json({ status: 'ok' });\n });\n\n /**\n * /report?entity=component:default/mycomponent\n */\n router.get('/report', async (req, res) => {\n const { entity } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n const stored = await codeCoverageDatabase.getCodeCoverage(entity as string);\n\n const aggregate = aggregateCoverage(stored);\n\n res.status(200).json({\n ...stored,\n aggregate: {\n line: aggregate.line,\n branch: aggregate.branch,\n },\n });\n });\n\n /**\n * /history?entity=component:default/mycomponent\n */\n router.get('/history', async (req, res) => {\n const { entity } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n const { limit } = req.query;\n const history = await codeCoverageDatabase.getHistory(\n entity as string,\n parseInt(limit?.toString() || '10', 10),\n );\n\n res.status(200).json(history);\n });\n\n /**\n * /file-content?entity=component:default/mycomponent&path=src/some-file.go\n */\n router.get('/file-content', async (req, res) => {\n const { entity, path } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n\n if (!path) {\n throw new InputError('Need path query parameter');\n }\n\n const sourceLocation = getEntitySourceLocation(entityLookup);\n\n if (!sourceLocation) {\n throw new InputError(\n `No \"backstage.io/source-location\" annotation on entity ${entity}`,\n );\n }\n\n const vcs = scm.byUrl(sourceLocation.target);\n if (!vcs) {\n throw new InputError(`Unable to determine SCM from ${sourceLocation}`);\n }\n\n const scmTree = await urlReader.readTree(sourceLocation.target);\n const scmFile = (await scmTree.files()).find(f => f.path === path);\n if (!scmFile) {\n res.status(404).json({\n message: \"Couldn't find file in SCM\",\n file: path,\n scm: vcs.title,\n });\n return;\n }\n const content = await scmFile?.content();\n if (!content) {\n res.status(400).json({\n message: \"Couldn't process content of file in SCM\",\n file: path,\n scm: vcs.title,\n });\n return;\n }\n\n const data = content.toString();\n res.status(200).contentType('text/plain').send(data);\n });\n\n /**\n * /report?entity=component:default/mycomponent&coverageType=cobertura\n */\n router.post('/report', async (req, res) => {\n const { entity: entityRef, coverageType } = req.query;\n const entity = await catalogApi.getEntityByRef(\n entityRef as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entity) {\n throw new NotFoundError(`No entity found matching ${entityRef}`);\n }\n\n let converter: Converter;\n if (!coverageType) {\n throw new InputError('Need coverageType query parameter');\n } else if (coverageType === 'jacoco') {\n converter = new Jacoco(logger);\n } else if (coverageType === 'cobertura') {\n converter = new Cobertura(logger);\n } else if (coverageType === 'lcov') {\n converter = new Lcov(logger);\n } else {\n throw new InputError(`Unsupported coverage type '${coverageType}`);\n }\n\n const { sourceLocation, vcs, scmFiles, body } =\n await utils.processCoveragePayload(entity, req);\n\n const files = converter.convert(body, scmFiles);\n if (!files || files.length === 0) {\n throw new InputError(`Unable to parse body as ${coverageType}`);\n }\n\n const coverage = await utils.buildCoverage(\n entity,\n sourceLocation,\n vcs,\n files,\n );\n await codeCoverageDatabase.insertCodeCoverage(coverage);\n\n res.status(201).json({\n links: [\n {\n rel: 'coverage',\n href: `${codecovUrl}/report?entity=${entityRef}`,\n },\n ],\n });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n};\n\n/**\n * Creates a code-coverage plugin backend router.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const logger = options.logger;\n\n logger.info('Initializing Code Coverage backend');\n\n return makeRouter(options);\n}\n"],"names":["CodeCoverageDatabase","CatalogClient","ScmIntegrations","createLegacyAuthAdapters","bodyParserXml","BodyParser","Router","express","CoverageUtils","NotFoundError","aggregateCoverage","InputError","getEntitySourceLocation","Jacoco","Cobertura","Lcov","MiddlewareFactory"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport BodyParser from 'body-parser';\nimport bodyParserXml from 'body-parser-xml';\nimport { CatalogApi, CatalogClient } from '@backstage/catalog-client';\nimport { createLegacyAuthAdapters } from '@backstage/backend-common';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { CodeCoverageDatabase } from './CodeCoverageDatabase';\nimport { aggregateCoverage, CoverageUtils } from './CoverageUtils';\nimport { Cobertura, Converter, Jacoco, Lcov } from './converter';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport {\n AuthService,\n DatabaseService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n UrlReaderService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\n\n/**\n * @deprecated Please migrate to the new backend system as this will be removed in the future.\n *\n * Options for {@link createRouter}.\n *\n * @public\n */\nexport interface RouterOptions {\n config: Config;\n discovery: DiscoveryService;\n database: DatabaseService;\n urlReader: UrlReaderService;\n logger: LoggerService;\n catalogApi?: CatalogApi;\n auth?: AuthService;\n httpAuth?: HttpAuthService;\n}\n\nexport interface CodeCoverageApi {\n name: string;\n}\n\nexport const makeRouter = async (\n options: RouterOptions,\n): Promise<express.Router> => {\n const { config, logger, discovery, database, urlReader } = options;\n\n const codeCoverageDatabase = await CodeCoverageDatabase.create(database);\n const codecovUrl = await discovery.getExternalBaseUrl('code-coverage');\n const catalogApi =\n options.catalogApi ?? new CatalogClient({ discoveryApi: discovery });\n const scm = ScmIntegrations.fromConfig(config);\n const { auth, httpAuth } = createLegacyAuthAdapters(options);\n\n const bodySizeLimit =\n config.getOptionalString('codeCoverage.bodySizeLimit') ?? '100kb';\n\n bodyParserXml(BodyParser);\n const router = Router();\n router.use(\n BodyParser.xml({\n limit: bodySizeLimit,\n }),\n );\n router.use(\n BodyParser.text({\n limit: bodySizeLimit,\n }),\n );\n router.use(express.json());\n\n const utils = new CoverageUtils(scm, urlReader);\n\n router.get('/health', async (_req, res) => {\n res.status(200).json({ status: 'ok' });\n });\n\n /**\n * /report?entity=component:default/mycomponent\n */\n router.get('/report', async (req, res) => {\n const { entity } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n const stored = await codeCoverageDatabase.getCodeCoverage(entity as string);\n\n const aggregate = aggregateCoverage(stored);\n\n res.status(200).json({\n ...stored,\n aggregate: {\n line: aggregate.line,\n branch: aggregate.branch,\n },\n });\n });\n\n /**\n * /history?entity=component:default/mycomponent\n */\n router.get('/history', async (req, res) => {\n const { entity } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n const { limit } = req.query;\n const history = await codeCoverageDatabase.getHistory(\n entity as string,\n parseInt(limit?.toString() || '10', 10),\n );\n\n res.status(200).json(history);\n });\n\n /**\n * /file-content?entity=component:default/mycomponent&path=src/some-file.go\n */\n router.get('/file-content', async (req, res) => {\n const { entity, path } = req.query;\n const entityLookup = await catalogApi.getEntityByRef(\n entity as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entityLookup) {\n throw new NotFoundError(`No entity found matching ${entity}`);\n }\n\n if (!path) {\n throw new InputError('Need path query parameter');\n }\n\n const sourceLocation = getEntitySourceLocation(entityLookup);\n\n if (!sourceLocation) {\n throw new InputError(\n `No \"backstage.io/source-location\" annotation on entity ${entity}`,\n );\n }\n\n const vcs = scm.byUrl(sourceLocation.target);\n if (!vcs) {\n throw new InputError(`Unable to determine SCM from ${sourceLocation}`);\n }\n\n const scmTree = await urlReader.readTree(sourceLocation.target);\n const scmFile = (await scmTree.files()).find(f => f.path === path);\n if (!scmFile) {\n res.status(404).json({\n message: \"Couldn't find file in SCM\",\n file: path,\n scm: vcs.title,\n });\n return;\n }\n const content = await scmFile?.content();\n if (!content) {\n res.status(400).json({\n message: \"Couldn't process content of file in SCM\",\n file: path,\n scm: vcs.title,\n });\n return;\n }\n\n const data = content.toString();\n res.status(200).contentType('text/plain').send(data);\n });\n\n /**\n * /report?entity=component:default/mycomponent&coverageType=cobertura\n */\n router.post('/report', async (req, res) => {\n const { entity: entityRef, coverageType } = req.query;\n const entity = await catalogApi.getEntityByRef(\n entityRef as string,\n await auth.getPluginRequestToken({\n onBehalfOf: await httpAuth.credentials(req),\n targetPluginId: 'catalog',\n }),\n );\n if (!entity) {\n throw new NotFoundError(`No entity found matching ${entityRef}`);\n }\n\n let converter: Converter;\n if (!coverageType) {\n throw new InputError('Need coverageType query parameter');\n } else if (coverageType === 'jacoco') {\n converter = new Jacoco(logger);\n } else if (coverageType === 'cobertura') {\n converter = new Cobertura(logger);\n } else if (coverageType === 'lcov') {\n converter = new Lcov(logger);\n } else {\n throw new InputError(`Unsupported coverage type '${coverageType}`);\n }\n\n const { sourceLocation, vcs, scmFiles, body } =\n await utils.processCoveragePayload(entity, req);\n\n const files = converter.convert(body, scmFiles);\n if (!files || files.length === 0) {\n throw new InputError(`Unable to parse body as ${coverageType}`);\n }\n\n const coverage = await utils.buildCoverage(\n entity,\n sourceLocation,\n vcs,\n files,\n );\n await codeCoverageDatabase.insertCodeCoverage(coverage);\n\n res.status(201).json({\n links: [\n {\n rel: 'coverage',\n href: `${codecovUrl}/report?entity=${entityRef}`,\n },\n ],\n });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n};\n\n/**\n * @deprecated Please migrate to the new backend system as this will be removed in the future.\n *\n * Creates a code-coverage plugin backend router.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const logger = options.logger;\n\n logger.info('Initializing Code Coverage backend');\n\n return makeRouter(options);\n}\n"],"names":["CodeCoverageDatabase","CatalogClient","ScmIntegrations","createLegacyAuthAdapters","bodyParserXml","BodyParser","Router","express","CoverageUtils","NotFoundError","aggregateCoverage","InputError","getEntitySourceLocation","Jacoco","Cobertura","Lcov","MiddlewareFactory"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage-community/plugin-code-coverage-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A Backstage backend plugin that helps you keep track of your code coverage",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@backstage/backend-common": "^0.25.0",
|
|
42
|
-
"@backstage/backend-defaults": "^0.
|
|
43
|
-
"@backstage/backend-plugin-api": "^1.0
|
|
44
|
-
"@backstage/catalog-client": "^1.
|
|
45
|
-
"@backstage/catalog-model": "^1.7.
|
|
46
|
-
"@backstage/config": "^1.
|
|
47
|
-
"@backstage/errors": "^1.2.
|
|
48
|
-
"@backstage/integration": "^1.
|
|
42
|
+
"@backstage/backend-defaults": "^0.6.1",
|
|
43
|
+
"@backstage/backend-plugin-api": "^1.1.0",
|
|
44
|
+
"@backstage/catalog-client": "^1.9.0",
|
|
45
|
+
"@backstage/catalog-model": "^1.7.2",
|
|
46
|
+
"@backstage/config": "^1.3.1",
|
|
47
|
+
"@backstage/errors": "^1.2.6",
|
|
48
|
+
"@backstage/integration": "^1.16.0",
|
|
49
49
|
"@types/express": "^4.17.6",
|
|
50
50
|
"body-parser": "^1.20.0",
|
|
51
51
|
"body-parser-xml": "^2.0.5",
|
|
@@ -56,12 +56,19 @@
|
|
|
56
56
|
"yn": "^4.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@backstage/backend-test-utils": "^1.0
|
|
60
|
-
"@backstage/cli": "^0.
|
|
59
|
+
"@backstage/backend-test-utils": "^1.2.0",
|
|
60
|
+
"@backstage/cli": "^0.29.4",
|
|
61
61
|
"@types/body-parser-xml": "^2.0.2",
|
|
62
62
|
"@types/supertest": "^6.0.0",
|
|
63
63
|
"@types/uuid": "^9.0.0",
|
|
64
64
|
"supertest": "^7.0.0",
|
|
65
65
|
"xml2js": "^0.6.0"
|
|
66
|
+
},
|
|
67
|
+
"typesVersions": {
|
|
68
|
+
"*": {
|
|
69
|
+
"index": [
|
|
70
|
+
"dist/index.d.ts"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
74
|
}
|