diff --git a/plugins/xcmetrics/src/api/XCMetricsClient.ts b/plugins/xcmetrics/src/api/XCMetricsClient.ts index f89cf00123..5d38108a0d 100644 --- a/plugins/xcmetrics/src/api/XCMetricsClient.ts +++ b/plugins/xcmetrics/src/api/XCMetricsClient.ts @@ -16,7 +16,7 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; -import { BuildItem, BuildsResult, XCMetricsApi } from './types'; +import { BuildCount, BuildItem, BuildsResult, XCMetricsApi } from './types'; interface Options { discoveryApi: DiscoveryApi; @@ -29,9 +29,9 @@ export class XCMetricsClient implements XCMetricsApi { this.discoveryApi = options.discoveryApi; } - async getBuilds(): Promise { + async getBuilds(limit = 10): Promise { const baseUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/xcmetrics`; - const response = await fetch(`${baseUrl}/build`); + const response = await fetch(`${baseUrl}/build?per=${limit}`); if (!response.ok) { throw await ResponseError.fromResponse(response); @@ -39,4 +39,17 @@ export class XCMetricsClient implements XCMetricsApi { return ((await response.json()) as BuildsResult).items; } + + async getBuildCounts(days: number): Promise { + const baseUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/xcmetrics`; + const response = await fetch( + `${baseUrl}/statistics/build/errors?days=${days}`, + ); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return (await response.json()) as BuildCount[]; + } } diff --git a/plugins/xcmetrics/src/api/types.ts b/plugins/xcmetrics/src/api/types.ts index 5df05f0375..ebdf00923e 100644 --- a/plugins/xcmetrics/src/api/types.ts +++ b/plugins/xcmetrics/src/api/types.ts @@ -44,6 +44,12 @@ export type BuildItem = { wasSuspended: boolean; }; +export type BuildCount = { + day: string; + errors: number; + builds: number; +}; + export type BuildsResult = { items: BuildItem[]; metadata: { @@ -54,7 +60,8 @@ export type BuildsResult = { }; export interface XCMetricsApi { - getBuilds(): Promise; + getBuilds(limit?: number): Promise; + getBuildCounts(days: number): Promise; } export const xcmetricsApiRef = createApiRef({ diff --git a/plugins/xcmetrics/src/components/ErrorTrendComponent/ErrorTrendComponent.tsx b/plugins/xcmetrics/src/components/ErrorTrendComponent/ErrorTrendComponent.tsx new file mode 100644 index 0000000000..b8f358a903 --- /dev/null +++ b/plugins/xcmetrics/src/components/ErrorTrendComponent/ErrorTrendComponent.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2021 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 { Progress, TrendLine } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; +import { BuildCount, xcmetricsApiRef } from '../../api'; +import { useAsync } from 'react-use'; +import { Alert } from '@material-ui/lab'; + +const TRENDLINE_TITLE = 'Error Rate'; + +interface ErrorTrendProps { + days: number; +} + +export const ErrorTrendComponent = ({ days }: ErrorTrendProps) => { + const client = useApi(xcmetricsApiRef); + const { value: buildCounts, loading, error } = useAsync( + async (): Promise => client.getBuildCounts(days), + [], + ); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } else if (!buildCounts) { + return ; + } + + let max = 0; + const averageErrors = buildCounts.map(counts => { + if (counts.builds === 0) return 0; + const dayAverage = counts.errors / counts.builds; + max = Math.max(max, dayAverage); + return dayAverage; + }); + + return ; +}; diff --git a/plugins/xcmetrics/src/components/ErrorTrendComponent/index.ts b/plugins/xcmetrics/src/components/ErrorTrendComponent/index.ts new file mode 100644 index 0000000000..974f40a8ae --- /dev/null +++ b/plugins/xcmetrics/src/components/ErrorTrendComponent/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 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 './ErrorTrendComponent'; diff --git a/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx b/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx index 18e65dd318..69eff0cead 100644 --- a/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx +++ b/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx @@ -24,13 +24,15 @@ import { Table, TableColumn, EmptyState, + InfoCard, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; import { BuildItem, BuildStatus, xcmetricsApiRef } from '../../api'; import { useAsync } from 'react-use'; import { Alert } from '@material-ui/lab'; import { Duration } from 'luxon'; -import { Chip } from '@material-ui/core'; +import { Chip, Grid, Typography } from '@material-ui/core'; +import { ErrorTrendComponent } from '../ErrorTrendComponent'; const formatStatus = (status: BuildStatus, warningCount: number) => { const statusIcons = { @@ -109,12 +111,22 @@ export const OverviewComponent = () => { Dashboard for XCMetrics - - options={{ paging: false, search: false }} - data={builds} - columns={columns} - title="Latest Builds" - /> + + + + options={{ paging: false, search: false }} + data={builds} + columns={columns} + title="Latest Builds" + /> + + + + Error Rate + + + + ); };