diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index 8b25800bb7..03be6dfba2 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -32,7 +32,7 @@ Configuration Details: Here's how to get the backend up and running: -1. First we need to add the @backstage/plugin-azure-devops-backend package to your backend: +1. First we need to add the `@backstage/plugin-azure-devops-backend` package to your backend: ```sh # From the Backstage root directory diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index bc39fd0ce4..000702268e 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -22,9 +22,7 @@ export const azureDevOpsPlugin: BackstagePlugin< // Warning: (ae-missing-release-tag) "EntityAzurePipelinesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const EntityAzurePipelinesContent: (_props: { - entity?: Entity | undefined; -}) => JSX.Element; +export const EntityAzurePipelinesContent: () => JSX.Element; // Warning: (ae-missing-release-tag) "isAzureDevOpsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 16008090b7..b6f7801766 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -4,6 +4,7 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", + "private": false, "publishConfig": { "access": "public", "main": "dist/index.esm.js", @@ -29,6 +30,7 @@ "@backstage/catalog-model": "^0.9.3", "@backstage/core-components": "^0.6.1", "@backstage/core-plugin-api": "^0.1.10", + "@backstage/errors": "^0.1.2", "@backstage/plugin-catalog-react": "^0.5.1", "@backstage/theme": "^0.2.10", "@material-ui/core": "^4.12.2", @@ -51,8 +53,8 @@ "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "msw": "^0.29.0", - "cross-fetch": "^3.0.6" + "cross-fetch": "^3.0.6", + "msw": "^0.29.0" }, "files": [ "dist", diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts index 424999441d..f0bad53b43 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { RepoBuild } from './types'; +import { RepoBuild, RepoBuildOptions } from './types'; import { createApiRef } from '@backstage/core-plugin-api'; export const azureDevOpsApiRef = createApiRef({ @@ -27,6 +27,6 @@ export interface AzureDevOpsApi { getRepoBuilds( projectName: string, repoName: string, - top: number, - ): Promise; + options?: RepoBuildOptions, + ): Promise<{ items: RepoBuild[] }>; } diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts index 02cac3b9de..f0ed2d9951 100644 --- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts +++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts @@ -15,9 +15,9 @@ */ import { AzureDevOpsApi } from './AzureDevOpsApi'; -import { RepoBuild } from './types'; +import { RepoBuild, RepoBuildOptions } from './types'; import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; -import { AzureDevOpsClientError } from './AzureDevOpsClientError'; +import { ResponseError } from '@backstage/errors'; export class AzureDevOpsClient implements AzureDevOpsApi { private readonly discoveryApi: DiscoveryApi; @@ -34,12 +34,14 @@ export class AzureDevOpsClient implements AzureDevOpsApi { getRepoBuilds( projectName: string, repoName: string, - top: number, - ): Promise { - return this.get(`repo-builds/${projectName}/${repoName}?top=${top}`); + options?: RepoBuildOptions, + ): Promise<{ items: RepoBuild[] }> { + return this.get( + `repo-builds/${projectName}/${repoName}?top=${options?.top}`, + ); } - private async get(path: string): Promise { + private async get(path: string): Promise<{ items: any }> { const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`; const url = new URL(path, baseUrl); @@ -49,10 +51,9 @@ export class AzureDevOpsClient implements AzureDevOpsApi { }); if (!response.ok) { - const payload = await response.text(); - throw new AzureDevOpsClientError(response, payload); + throw await ResponseError.fromResponse(response); } - return response.json() as Promise; + return response.json() as Promise<{ items: any }>; } } diff --git a/plugins/azure-devops/src/api/AzureDevOpsClientError.ts b/plugins/azure-devops/src/api/AzureDevOpsClientError.ts deleted file mode 100644 index f9a2f37b41..0000000000 --- a/plugins/azure-devops/src/api/AzureDevOpsClientError.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 class AzureDevOpsClientError extends Error { - public constructor({ status, statusText }: Response, payload: string) { - super(`Request failed with ${status} ${statusText}, ${payload}`); - } -} diff --git a/plugins/azure-devops/src/api/types.ts b/plugins/azure-devops/src/api/types.ts index b3562f76c6..1fbc34231e 100644 --- a/plugins/azure-devops/src/api/types.ts +++ b/plugins/azure-devops/src/api/types.ts @@ -28,3 +28,7 @@ export type RepoBuild = { queueTime?: Date; source: string; }; + +export type RepoBuildOptions = { + top?: number; +}; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index a6cfb9bf81..bead424ffb 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -17,6 +17,7 @@ import React from 'react'; import { DateTime } from 'luxon'; import { + Link, Table, TableColumn, StatusError, @@ -25,9 +26,9 @@ import { StatusAborted, StatusRunning, StatusPending, + ResponseErrorPanel, } from '@backstage/core-components'; -import { Link, Box, Typography } from '@material-ui/core'; -import Alert from '@material-ui/lab/Alert'; +import { Box, Typography } from '@material-ui/core'; import { RepoBuild } from '../../api/types'; import { BuildResult, @@ -39,25 +40,25 @@ const getBuildResultComponent = (result: number | undefined) => { case BuildResult.Succeeded: return ( - Succeeded{' '} + Succeeded ); case BuildResult.PartiallySucceeded: return ( - Partially Succeeded{' '} + Partially Succeeded ); case BuildResult.Failed: return ( - Failed{' '} + Failed ); case BuildResult.Canceled: return ( - Canceled{' '} + Canceled ); case BuildResult.None: @@ -123,9 +124,7 @@ const columns: TableColumn[] = [ field: 'title', width: 'auto', render: (row: Partial) => ( - - {row.title} - + {row.title} ), }, { @@ -165,10 +164,7 @@ export const BuildTable = ({ items, loading, error }: Props) => { if (error) { return (
- - Error encountered while fetching Azure DevOps builds.{' '} - {error.toString()} - +
); } diff --git a/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx b/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx index 5d53df928c..34c8bc4fd1 100644 --- a/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx +++ b/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx @@ -14,18 +14,12 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; import React from 'react'; import { useRepoBuilds } from '../../hooks/useRepoBuilds'; import { BuildTable } from '../BuildTable/BuildTable'; -type Props = { - /** @deprecated The entity is now grabbed from context instead */ - entity?: Entity; -}; - -export const EntityPageAzurePipelines = (_props: Props) => { +export const EntityPageAzurePipelines = () => { const { entity } = useEntity(); const { items, loading, error } = useRepoBuilds(entity); diff --git a/plugins/azure-devops/src/components/Router.tsx b/plugins/azure-devops/src/components/Router.tsx index 81b6d89a25..ce8dd3b51d 100644 --- a/plugins/azure-devops/src/components/Router.tsx +++ b/plugins/azure-devops/src/components/Router.tsx @@ -25,12 +25,7 @@ import { MissingAnnotationEmptyState } from '@backstage/core-components'; export const isAzureDevOpsAvailable = (entity: Entity) => Boolean(entity.metadata.annotations?.[AZURE_DEVOPS_ANNOTATION]); -type Props = { - /** @deprecated The entity is now grabbed from context instead */ - entity?: Entity; -}; - -export const Router = (_props: Props) => { +export const Router = () => { const { entity } = useEntity(); if (!isAzureDevOpsAvailable(entity)) { diff --git a/plugins/azure-devops/src/hooks/useRepoBuilds.ts b/plugins/azure-devops/src/hooks/useRepoBuilds.ts index 0bae4b334b..8dafccb4a2 100644 --- a/plugins/azure-devops/src/hooks/useRepoBuilds.ts +++ b/plugins/azure-devops/src/hooks/useRepoBuilds.ts @@ -18,7 +18,7 @@ import { useAsync } from 'react-use'; import { Entity } from '@backstage/catalog-model'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { azureDevOpsApiRef } from '../api'; -import { RepoBuild } from '../api/types'; +import { RepoBuild, RepoBuildOptions } from '../api/types'; import { useProjectRepoFromEntity } from './useProjectRepoFromEntity'; import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; @@ -31,14 +31,17 @@ export function useRepoBuilds(entity: Entity): { const top = config.getOptionalNumber('azureDevOps.azurePipelines.top') ?? AZURE_DEVOPS_DEFAULT_TOP; + const options: RepoBuildOptions = { + top: top, + }; const api = useApi(azureDevOpsApiRef); const { project, repo } = useProjectRepoFromEntity(entity); const { value, loading, error } = useAsync(() => { - return api.getRepoBuilds(project, repo, top); + return api.getRepoBuilds(project, repo, options); }, [api, project, repo, entity]); return { - items: value as RepoBuild[], + items: value?.items as RepoBuild[], loading, error, };