diff --git a/plugins/xcmetrics/src/api/XcmetricsClient.ts b/plugins/xcmetrics/src/api/XcmetricsClient.ts index 2fcdaa3150..998244f967 100644 --- a/plugins/xcmetrics/src/api/XcmetricsClient.ts +++ b/plugins/xcmetrics/src/api/XcmetricsClient.ts @@ -18,6 +18,7 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; import { Build, + BuildCount, BuildStatusResult, PaginationResult, XcmetricsApi, @@ -56,6 +57,19 @@ export class XcmetricsClient implements XcmetricsApi { return ((await response.json()) as PaginationResult).items; } + async getBuildCounts(days: number): Promise { + const baseUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/xcmetrics`; + const response = await fetch( + `${baseUrl}/statistics/build/count?days=${days}`, + ); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return (await response.json()) as BuildCount[]; + } + async getBuildStatuses(limit: number): Promise { const baseUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/xcmetrics`; const response = await fetch( diff --git a/plugins/xcmetrics/src/api/types.ts b/plugins/xcmetrics/src/api/types.ts index d995c53724..487da5065d 100644 --- a/plugins/xcmetrics/src/api/types.ts +++ b/plugins/xcmetrics/src/api/types.ts @@ -46,6 +46,12 @@ export type Build = { export type BuildStatusResult = Pick; +export type BuildCount = { + day: string; + errors: number; + builds: number; +}; + export type PaginationResult = { items: T[]; metadata: { @@ -58,6 +64,7 @@ export type PaginationResult = { export interface XcmetricsApi { getBuild(id: string): Promise; getBuilds(): Promise; + getBuildCounts(days: number): Promise; getBuildStatuses(limit: number): Promise; } 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 613a7a031a..b9bd048ac1 100644 --- a/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx +++ b/plugins/xcmetrics/src/components/OverviewComponent/OverviewComponent.tsx @@ -24,14 +24,16 @@ import { Table, TableColumn, EmptyState, + InfoCard, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; import { Build, BuildStatus, xcmetricsApiRef } from '../../api'; import { useAsync } from 'react-use'; import { Alert } from '@material-ui/lab'; -import { Chip } from '@material-ui/core'; import { StatusMatrixComponent } from '../StatusMatrixComponent'; import { formatDuration, formatStatus } from '../../utils'; +import { Chip, Grid, Typography } from '@material-ui/core'; +import { ErrorTrendComponent } from '../ErrorTrendComponent'; const Status = ({ status, @@ -118,17 +120,27 @@ export const OverviewComponent = () => { Dashboard for XCMetrics - - Latest Builds - - - } - /> + + +
+ Latest Builds + + + } + /> + + + + Error Rate + + + + ); };