@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 { CodeClimateData } from './code-climate-data';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const codeClimateApiRef = createApiRef<CodeClimateApi>({
|
||||
id: 'plugin.code-climate.service',
|
||||
});
|
||||
|
||||
export interface CodeClimateApi {
|
||||
fetchData(repoID: string): Promise<CodeClimateData>;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type CodeClimateRepoData = {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
analysis_version: number;
|
||||
badge_token: string;
|
||||
branch: string;
|
||||
created_at: string;
|
||||
delegated_config_repo_id: string;
|
||||
diff_coverage_enforced: boolean;
|
||||
diff_coverage_threshold: number;
|
||||
enable_notifications: boolean;
|
||||
github_slug: string;
|
||||
human_name: string;
|
||||
last_activity_at: string;
|
||||
test_reporter_id: string | null;
|
||||
total_coverage_enforced: boolean;
|
||||
vcs_database_id: string;
|
||||
vcs_host: string;
|
||||
score: string | null;
|
||||
};
|
||||
relationships: {
|
||||
latest_default_branch_snapshot: {
|
||||
data: { id: string; type: string };
|
||||
};
|
||||
latest_default_branch_test_report: {
|
||||
data: { id: string; type: string };
|
||||
};
|
||||
account: {
|
||||
data: { id: string; type: string };
|
||||
};
|
||||
};
|
||||
links: {
|
||||
self: string;
|
||||
services: string;
|
||||
web_coverage: string;
|
||||
web_issues: string;
|
||||
maintainability_badge: string;
|
||||
test_coverage_badge: string;
|
||||
};
|
||||
meta: { permissions: { admin: boolean } };
|
||||
};
|
||||
};
|
||||
|
||||
export type CodeClimateMaintainabilityData = {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
commit_sha: string;
|
||||
committed_at: string;
|
||||
created_at: string;
|
||||
lines_of_code: number;
|
||||
ratings: [
|
||||
{
|
||||
path: string;
|
||||
letter: string;
|
||||
measure: {
|
||||
value: number;
|
||||
unit: string;
|
||||
meta: {
|
||||
remediation_time: { value: number; unit: string };
|
||||
implementation_time: {
|
||||
value: number;
|
||||
unit: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
pillar: string;
|
||||
},
|
||||
];
|
||||
gpa: string | null;
|
||||
worker_version: number;
|
||||
};
|
||||
meta: {
|
||||
issues_count: number;
|
||||
measures: {
|
||||
remediation: { value: number; unit: string };
|
||||
technical_debt_ratio: {
|
||||
value: number;
|
||||
unit: string;
|
||||
meta: {
|
||||
remediation_time: { value: number; unit: string };
|
||||
implementation_time: {
|
||||
value: number;
|
||||
unit: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type CodeClimateTestCoverageData = {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
branch: string;
|
||||
commit_sha: string;
|
||||
committed_at: string;
|
||||
covered_percent: number;
|
||||
lines_of_code: number;
|
||||
rating: {
|
||||
path: string;
|
||||
letter: string;
|
||||
measure: { value: number; unit: string };
|
||||
pillar: string;
|
||||
};
|
||||
received_at: string;
|
||||
state: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type CodeClimateIssuesData = {
|
||||
data: [
|
||||
{
|
||||
id: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
categories: string[];
|
||||
check_name: string;
|
||||
constant_name: string;
|
||||
content: { body: string };
|
||||
description: string;
|
||||
engine_name: string;
|
||||
fingerprint: string;
|
||||
location: {
|
||||
path: string;
|
||||
end_line: number;
|
||||
start_line: number;
|
||||
};
|
||||
other_locations: string[];
|
||||
remediation_points: number;
|
||||
severity: string;
|
||||
};
|
||||
meta: { permissions: { manageable: boolean } };
|
||||
},
|
||||
];
|
||||
links: {
|
||||
self: string;
|
||||
next: string;
|
||||
last: string;
|
||||
};
|
||||
meta: { current_page: number; total_pages: number; total_count: number };
|
||||
};
|
||||
|
||||
export type CodeClimateData = {
|
||||
repoID: string;
|
||||
maintainability: {
|
||||
letter: string;
|
||||
value: string;
|
||||
};
|
||||
testCoverage: {
|
||||
letter: string;
|
||||
value: string;
|
||||
};
|
||||
numberOfCodeSmells: number;
|
||||
numberOfDuplication: number;
|
||||
numberOfOtherIssues: number;
|
||||
};
|
||||
|
||||
export type CodeClimateApiError = {
|
||||
detail: string;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './mock';
|
||||
export type { CodeClimateApi } from './code-climate-api';
|
||||
export { codeClimateApiRef } from './code-climate-api';
|
||||
export type { CodeClimateData } from './code-climate-data';
|
||||
export { ProductionCodeClimateApi } from './production-api';
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"data": {
|
||||
"id": "6b8cc37a64b741dd9d516119",
|
||||
"type": "snapshots",
|
||||
"attributes": {
|
||||
"commit_sha": "d670460b4b4aece5915caf5c68d12f560a9fe3e4",
|
||||
"committed_at": "2022-01-14T15:17:29.311Z",
|
||||
"created_at": "2022-01-31T10:07:40.415Z",
|
||||
"lines_of_code": 8854,
|
||||
"ratings": [
|
||||
{
|
||||
"path": "/",
|
||||
"letter": "B",
|
||||
"measure": {
|
||||
"value": 5.264879308188034,
|
||||
"unit": "percent",
|
||||
"meta": {
|
||||
"remediation_time": { "value": 7186.52, "unit": "minute" },
|
||||
"implementation_time": {
|
||||
"value": 136499.2353922225,
|
||||
"unit": "minute"
|
||||
}
|
||||
}
|
||||
},
|
||||
"pillar": "Maintainability"
|
||||
}
|
||||
],
|
||||
"gpa": null,
|
||||
"worker_version": 69440
|
||||
},
|
||||
"meta": {
|
||||
"issues_count": 127,
|
||||
"measures": {
|
||||
"remediation": { "value": 7186.52, "unit": "minute" },
|
||||
"technical_debt_ratio": {
|
||||
"value": 5.264879308188034,
|
||||
"unit": "percent",
|
||||
"meta": {
|
||||
"remediation_time": { "value": 7186.52, "unit": "minute" },
|
||||
"implementation_time": {
|
||||
"value": 136499.2353922225,
|
||||
"unit": "minute"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"id": "6b8cc37a64b741dd9d516119",
|
||||
"type": "test_reports",
|
||||
"attributes": {
|
||||
"branch": "master",
|
||||
"commit_sha": "d670460b4b4aece5915caf5c68d12f560a9fe3e4",
|
||||
"committed_at": "2022-01-14T15:17:26.000Z",
|
||||
"covered_percent": 88.41059602649007,
|
||||
"lines_of_code": 8854,
|
||||
"rating": {
|
||||
"path": "/",
|
||||
"letter": "B",
|
||||
"measure": { "value": 88.41059602649007, "unit": "percent" },
|
||||
"pillar": "Test Coverage"
|
||||
},
|
||||
"received_at": "2022-01-14T15:25:38.352Z",
|
||||
"state": "done"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { mockData } from './mock-api';
|
||||
export { MockCodeClimateApi } from './mock-api';
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 { CodeClimateData } from '../code-climate-data';
|
||||
import { CodeClimateApi } from '../code-climate-api';
|
||||
import maintainabilityMock from './code-climate-maintainability-mock.json';
|
||||
import testCoverageMock from './code-climate-test-coverage-mock.json';
|
||||
import { Duration } from 'luxon';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
|
||||
const maintainabilityData = maintainabilityMock.data.attributes.ratings[0];
|
||||
const testCoverageData = testCoverageMock.data.attributes.rating;
|
||||
|
||||
const maintainabilityValue: any = {};
|
||||
maintainabilityValue[
|
||||
maintainabilityData.measure.meta.implementation_time.unit
|
||||
] = maintainabilityData.measure.meta.implementation_time.value.toFixed();
|
||||
|
||||
export const mockData: CodeClimateData = {
|
||||
repoID: '6b8cc37a64b741dd9d516119',
|
||||
maintainability: {
|
||||
letter: maintainabilityData.letter,
|
||||
value: humanizeDuration(
|
||||
Duration.fromObject(maintainabilityValue).toMillis(),
|
||||
{ largest: 1 },
|
||||
),
|
||||
},
|
||||
testCoverage: {
|
||||
letter: testCoverageData.letter,
|
||||
value: testCoverageData.measure.value.toFixed(),
|
||||
},
|
||||
numberOfCodeSmells: 97,
|
||||
numberOfDuplication: 49,
|
||||
numberOfOtherIssues: 26,
|
||||
};
|
||||
|
||||
export class MockCodeClimateApi implements CodeClimateApi {
|
||||
fetchData(): Promise<CodeClimateData> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(mockData), 800);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 {
|
||||
CodeClimateData,
|
||||
CodeClimateRepoData,
|
||||
CodeClimateMaintainabilityData,
|
||||
CodeClimateTestCoverageData,
|
||||
CodeClimateIssuesData,
|
||||
} from './code-climate-data';
|
||||
import { CodeClimateApi } from './code-climate-api';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { Duration } from 'luxon';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
|
||||
const pageQuery = encodeURIComponent('page[size]');
|
||||
const statusFilter = encodeURIComponent('filter[status][$in][]');
|
||||
const categoriesFilter = encodeURIComponent('filter[categories][$in][]');
|
||||
const basicIssuesOptions = `${pageQuery}=1&${statusFilter}=open&${statusFilter}=confirmed`;
|
||||
const codeSmellsQuery = `${basicIssuesOptions}&${categoriesFilter}=Complexity`;
|
||||
const duplicationQuery = `${basicIssuesOptions}&${categoriesFilter}=Duplication`;
|
||||
const otherIssuesQuery = `${basicIssuesOptions}&${categoriesFilter}=Bug%20Risk`;
|
||||
|
||||
export class ProductionCodeClimateApi implements CodeClimateApi {
|
||||
constructor(private readonly discoveryApi: DiscoveryApi) {}
|
||||
|
||||
async fetchAllData(options: {
|
||||
apiUrl: string;
|
||||
repoID: string;
|
||||
snapshotID: string;
|
||||
testReportID: string;
|
||||
}): Promise<any> {
|
||||
const { apiUrl, repoID, snapshotID, testReportID } = options;
|
||||
|
||||
const [
|
||||
maintainabilityResponse,
|
||||
testCoverageResponse,
|
||||
codeSmellsResponse,
|
||||
duplicationResponse,
|
||||
otherIssuesResponse,
|
||||
] = await Promise.all([
|
||||
await fetch(`${apiUrl}/repos/${repoID}/snapshots/${snapshotID}`),
|
||||
await fetch(`${apiUrl}/repos/${repoID}/test_reports/${testReportID}`),
|
||||
await fetch(
|
||||
`${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${codeSmellsQuery}`,
|
||||
),
|
||||
await fetch(
|
||||
`${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${duplicationQuery}`,
|
||||
),
|
||||
await fetch(
|
||||
`${apiUrl}/repos/${repoID}/snapshots/${snapshotID}/issues?${otherIssuesQuery}`,
|
||||
),
|
||||
]);
|
||||
|
||||
if (
|
||||
!maintainabilityResponse.ok ||
|
||||
!testCoverageResponse.ok ||
|
||||
!codeSmellsResponse.ok ||
|
||||
!duplicationResponse.ok ||
|
||||
!otherIssuesResponse.ok
|
||||
) {
|
||||
throw new Error('Failed fetching Code Climate info');
|
||||
}
|
||||
|
||||
const maintainabilityData = (
|
||||
(await maintainabilityResponse.json()) as CodeClimateMaintainabilityData
|
||||
).data.attributes.ratings[0];
|
||||
const testCoverageData = (
|
||||
(await testCoverageResponse.json()) as CodeClimateTestCoverageData
|
||||
).data.attributes.rating;
|
||||
const codeSmellsData = (
|
||||
(await codeSmellsResponse.json()) as CodeClimateIssuesData
|
||||
).meta.total_count;
|
||||
const duplicationData = (
|
||||
(await duplicationResponse.json()) as CodeClimateIssuesData
|
||||
).meta.total_count;
|
||||
const otherIssuesData = (
|
||||
(await otherIssuesResponse.json()) as CodeClimateIssuesData
|
||||
).meta.total_count;
|
||||
|
||||
return [
|
||||
maintainabilityData,
|
||||
testCoverageData,
|
||||
codeSmellsData,
|
||||
duplicationData,
|
||||
otherIssuesData,
|
||||
];
|
||||
}
|
||||
|
||||
async fetchData(repoID: string): Promise<CodeClimateData> {
|
||||
if (!repoID) {
|
||||
throw new Error('No Repo id found');
|
||||
}
|
||||
|
||||
const apiUrl = `${await this.discoveryApi.getBaseUrl(
|
||||
'proxy',
|
||||
)}/codeclimate/api`;
|
||||
|
||||
const repoResponse = await fetch(`${apiUrl}/repos/${repoID}`);
|
||||
|
||||
if (!repoResponse.ok) {
|
||||
throw new Error('Failed fetching Code Climate info');
|
||||
}
|
||||
|
||||
const repoData = ((await repoResponse.json()) as CodeClimateRepoData).data;
|
||||
const snapshotID =
|
||||
repoData.relationships.latest_default_branch_snapshot.data.id;
|
||||
const testReportID =
|
||||
repoData.relationships.latest_default_branch_test_report.data.id;
|
||||
|
||||
const [
|
||||
maintainabilityData,
|
||||
testCoverageData,
|
||||
codeSmellsData,
|
||||
duplicationData,
|
||||
otherIssuesData,
|
||||
] = await this.fetchAllData({
|
||||
apiUrl,
|
||||
repoID,
|
||||
snapshotID,
|
||||
testReportID,
|
||||
});
|
||||
|
||||
const maintainabilityValue: any = {};
|
||||
maintainabilityValue[
|
||||
maintainabilityData.measure.meta.implementation_time.unit
|
||||
] = maintainabilityData.measure.meta.implementation_time.value.toFixed();
|
||||
|
||||
return {
|
||||
repoID,
|
||||
maintainability: {
|
||||
letter: maintainabilityData.letter,
|
||||
value: humanizeDuration(
|
||||
Duration.fromObject(maintainabilityValue).toMillis(),
|
||||
{ largest: 1 },
|
||||
),
|
||||
},
|
||||
testCoverage: {
|
||||
letter: testCoverageData.letter,
|
||||
value: testCoverageData.measure.value.toFixed(),
|
||||
},
|
||||
numberOfCodeSmells: codeSmellsData,
|
||||
numberOfDuplication: duplicationData,
|
||||
numberOfOtherIssues: otherIssuesData,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { InfoCard } from '@backstage/core-components';
|
||||
import { CodeClimateCardContents } from '../CodeClimateCardContents';
|
||||
|
||||
export const CodeClimateCard = () => (
|
||||
<InfoCard title="Code Climate Summary">
|
||||
<CodeClimateCardContents />
|
||||
</InfoCard>
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { CodeClimateCard } from './CodeClimateCard';
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { codeClimateApiRef } from '../../api';
|
||||
import { CodeClimateTable } from '../CodeClimateTable';
|
||||
import { CODECLIMATE_REPO_ID_ANNOTATION } from '../../plugin';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
EmptyState,
|
||||
ErrorPanel,
|
||||
MissingAnnotationEmptyState,
|
||||
Progress,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const CodeClimateCardContents = () => {
|
||||
const { entity } = useEntity();
|
||||
const codeClimateApi = useApi(codeClimateApiRef);
|
||||
|
||||
const repoID =
|
||||
entity?.metadata.annotations?.[CODECLIMATE_REPO_ID_ANNOTATION] ?? '';
|
||||
|
||||
const { loading, value, error } = useAsync(
|
||||
() => codeClimateApi.fetchData(repoID),
|
||||
[codeClimateApi, repoID],
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
} else if (!repoID) {
|
||||
return (
|
||||
<MissingAnnotationEmptyState
|
||||
annotation={CODECLIMATE_REPO_ID_ANNOTATION}
|
||||
/>
|
||||
);
|
||||
} else if (error) {
|
||||
return <ErrorPanel error={error} />;
|
||||
} else if (!value) {
|
||||
return (
|
||||
<EmptyState
|
||||
missing="info"
|
||||
title="No information to display"
|
||||
description={`There is no Code Climate repo setup with id '${repoID}'.`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <CodeClimateTable codeClimateData={value} />;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { CodeClimateCardContents } from './CodeClimateCardContents';
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { CodeClimateTable } from './CodeClimateTable';
|
||||
import { mockData } from '../../api/mock';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
|
||||
describe('CodeClimateTable', () => {
|
||||
it('should render values in a table', async () => {
|
||||
const table = await render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CodeClimateTable codeClimateData={mockData} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(await table.findByText('3 months')).toBeInTheDocument();
|
||||
expect(await table.findByText('88%')).toBeInTheDocument();
|
||||
expect(await table.findByText('97')).toBeInTheDocument();
|
||||
expect(await table.findByText('49')).toBeInTheDocument();
|
||||
expect(await table.findByText('26')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { CodeClimateData } from '../../api';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { Box, makeStyles, Typography } from '@material-ui/core';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
|
||||
const letterStyle = {
|
||||
color: 'white',
|
||||
border: 0,
|
||||
borderRadius: '3px',
|
||||
fontSize: '40px',
|
||||
padding: '5px 20px',
|
||||
};
|
||||
|
||||
const fontSize = {
|
||||
fontSize: '25px',
|
||||
};
|
||||
|
||||
const letterColor = (letter: string) => {
|
||||
if (letter === 'A') {
|
||||
return '#45d298';
|
||||
} else if (letter === 'B') {
|
||||
return '#a5d86e';
|
||||
} else if (letter === 'C') {
|
||||
return '#f1ce0c';
|
||||
} else if (letter === 'D') {
|
||||
return '#f29141';
|
||||
} else if (letter === 'F') {
|
||||
return '#df5869';
|
||||
}
|
||||
|
||||
return '#45d298';
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<
|
||||
BackstageTheme,
|
||||
{
|
||||
maintainabilityLetter: string;
|
||||
testCoverageLetter: string;
|
||||
}
|
||||
>({
|
||||
spaceAround: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
spaceBetween: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
maintainabilityLetterColor: {
|
||||
...letterStyle,
|
||||
backgroundColor: props => letterColor(props.maintainabilityLetter),
|
||||
},
|
||||
testCoverageLetterColor: {
|
||||
...letterStyle,
|
||||
backgroundColor: props => letterColor(props.testCoverageLetter),
|
||||
},
|
||||
fontSize: {
|
||||
...fontSize,
|
||||
},
|
||||
letterDetails: {
|
||||
...fontSize,
|
||||
paddingLeft: '10px',
|
||||
},
|
||||
paddingSides20: {
|
||||
padding: '0px 20px',
|
||||
},
|
||||
});
|
||||
|
||||
export const CodeClimateTable = ({
|
||||
codeClimateData,
|
||||
}: {
|
||||
codeClimateData: CodeClimateData;
|
||||
}) => {
|
||||
const {
|
||||
repoID,
|
||||
maintainability: {
|
||||
letter: maintainabilityLetter,
|
||||
value: maintainabilityValue,
|
||||
},
|
||||
testCoverage: { letter: testCoverageLetter, value: testCoverageValue },
|
||||
numberOfCodeSmells,
|
||||
numberOfDuplication,
|
||||
numberOfOtherIssues,
|
||||
} = codeClimateData;
|
||||
|
||||
const classes = useStyles({ maintainabilityLetter, testCoverageLetter });
|
||||
|
||||
if (!codeClimateData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.spaceAround}>
|
||||
<div>
|
||||
<Typography variant="h6" component="p">
|
||||
Maintainability
|
||||
</Typography>
|
||||
<div className={classes.spaceBetween}>
|
||||
<Typography
|
||||
className={classes.maintainabilityLetterColor}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{maintainabilityLetter}
|
||||
</Typography>
|
||||
<Link to={`https://codeclimate.com/repos/${repoID}`}>
|
||||
<Typography
|
||||
className={classes.letterDetails}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{maintainabilityValue}
|
||||
</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="h6" component="p">
|
||||
Test Coverage
|
||||
</Typography>
|
||||
<div className={classes.spaceBetween}>
|
||||
<Typography
|
||||
className={classes.testCoverageLetterColor}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{testCoverageLetter}
|
||||
</Typography>
|
||||
<Link to={`https://codeclimate.com/repos/${repoID}`}>
|
||||
<Typography
|
||||
className={classes.letterDetails}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{testCoverageValue}%
|
||||
</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Box className={classes.spaceAround} paddingTop="30px">
|
||||
<div>
|
||||
<Typography variant="h6" component="p">
|
||||
Code Smells:
|
||||
</Typography>
|
||||
<Link
|
||||
to={`https://codeclimate.com/repos/${repoID}/issues?category%5B%5D=complexity&status%5B%5D=&status%5B%5D=open&status%5B%5D=confirmed`}
|
||||
>
|
||||
<Typography
|
||||
className={classes.fontSize}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{numberOfCodeSmells}
|
||||
</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
<Box paddingLeft="20" paddingRight="20">
|
||||
<Typography variant="h6" component="p">
|
||||
Duplication:
|
||||
</Typography>
|
||||
<Link
|
||||
to={`https://codeclimate.com/repos/${repoID}/issues?category%5B%5D=duplication&status%5B%5D=&status%5B%5D=open&status%5B%5D=confirmed`}
|
||||
>
|
||||
<Typography
|
||||
className={classes.fontSize}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{numberOfDuplication}
|
||||
</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
<div>
|
||||
<Typography variant="h6" component="p">
|
||||
Other Issues:
|
||||
</Typography>
|
||||
<Link
|
||||
to={`https://codeclimate.com/repos/${repoID}/issues?category%5B%5D=bugrisk&status%5B%5D=&status%5B%5D=open&status%5B%5D=confirmed`}
|
||||
>
|
||||
<Typography
|
||||
className={classes.fontSize}
|
||||
variant="body2"
|
||||
component="p"
|
||||
>
|
||||
{numberOfOtherIssues}
|
||||
</Typography>
|
||||
</Link>
|
||||
</div>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { CodeClimateTable } from './CodeClimateTable';
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Backstage plugin that integrates towards Code Climate
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './api';
|
||||
export { codeClimatePlugin, EntityCodeClimateCard } from './plugin';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 { codeClimatePlugin } from './plugin';
|
||||
|
||||
describe('codeclimate', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(codeClimatePlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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 { ProductionCodeClimateApi, codeClimateApiRef } from './api';
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
createComponentExtension,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
export const CODECLIMATE_REPO_ID_ANNOTATION = 'codeclimate.com/repo-id';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'code-climate',
|
||||
});
|
||||
|
||||
export const codeClimatePlugin = createPlugin({
|
||||
id: 'code-climate',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: codeClimateApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
identityApi: identityApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi }) => new ProductionCodeClimateApi(discoveryApi),
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const EntityCodeClimateCard = codeClimatePlugin.provide(
|
||||
createComponentExtension({
|
||||
name: 'EntityCodeClimateCard',
|
||||
component: {
|
||||
lazy: () =>
|
||||
import('./components/CodeClimateCard').then(m => m.CodeClimateCard),
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
import 'cross-fetch/polyfill';
|
||||
Reference in New Issue
Block a user