diff --git a/.changeset/wise-countries-watch.md b/.changeset/wise-countries-watch.md
new file mode 100644
index 0000000000..e48e7bbecb
--- /dev/null
+++ b/.changeset/wise-countries-watch.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-azure-devops': patch
+'@backstage/plugin-azure-devops-backend': patch
+'@backstage/plugin-azure-devops-common': patch
+---
+
+Added entity view for Azure Git Tags, based on existing Pull Requests view
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index 26068c6c8f..22d6cc3ab1 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -36,6 +36,7 @@ import {
} from '@backstage/plugin-api-docs';
import {
EntityAzurePipelinesContent,
+ EntityAzureGitTagsContent,
EntityAzurePullRequestsContent,
isAzureDevOpsAvailable,
isAzurePipelinesAvailable,
@@ -478,6 +479,14 @@ const websiteEntityPage = (
+
+
+
+
{pullRequestsContent}
diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md
index f9b8b75b82..e3e3d9e161 100644
--- a/plugins/azure-devops-backend/api-report.md
+++ b/plugins/azure-devops-backend/api-report.md
@@ -10,6 +10,7 @@ import { Config } from '@backstage/config';
import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common';
import express from 'express';
import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces';
+import { GitTag } from '@backstage/plugin-azure-devops-common';
import { Logger } from 'winston';
import { PullRequest } from '@backstage/plugin-azure-devops-common';
import { PullRequestOptions } from '@backstage/plugin-azure-devops-common';
@@ -61,6 +62,8 @@ export class AzureDevOpsApi {
repoName: string,
): Promise;
// (undocumented)
+ getGitTags(projectName: string, repoName: string): Promise;
+ // (undocumented)
getPullRequests(
projectName: string,
repoName: string,
diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
index 76b5089bbc..dade60f1ed 100644
--- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
+++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
@@ -21,16 +21,19 @@ import {
import {
BuildResult,
BuildStatus,
+ GitTag,
PullRequest,
PullRequestStatus,
RepoBuild,
} from '@backstage/plugin-azure-devops-common';
import {
GitPullRequest,
+ GitRef,
GitRepository,
} from 'azure-devops-node-api/interfaces/GitInterfaces';
import {
mappedBuildRun,
+ mappedGitTag,
mappedPullRequest,
mappedRepoBuild,
} from './AzureDevOpsApi';
@@ -263,6 +266,38 @@ describe('AzureDevOpsApi', () => {
});
});
+ describe('mappedGitTag', () => {
+ describe('mappedGitTag happy path', () => {
+ it('should return GitTag from GitRef', () => {
+ const inputIdentityRef: IdentityRef = {
+ displayName: 'Jane Doe',
+ };
+ const inputGitRef: GitRef = {
+ name: 'refs/tags/v1.1.2',
+ creator: inputIdentityRef,
+ objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee',
+ peeledObjectId: '1234567890abcdef1234567890abcdef12345678',
+ };
+ const inputLinkBaseUrl =
+ 'https://host.com/myOrg/_git/super-feature-repo?version=GT';
+ const inputCommitBaseUrl =
+ 'https://host.com/myOrg/_git/super-feature-repo/commit';
+ const outputGitTag: GitTag = {
+ name: 'v1.1.2',
+ createdBy: 'Jane Doe',
+ commitLink:
+ 'https://host.com/myOrg/_git/super-feature-repo/commit/1234567890abcdef1234567890abcdef12345678',
+ objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee',
+ peeledObjectId: '1234567890abcdef1234567890abcdef12345678',
+ link: 'https://host.com/myOrg/_git/super-feature-repo?version=GTv1.1.2',
+ };
+ expect(
+ mappedGitTag(inputGitRef, inputLinkBaseUrl, inputCommitBaseUrl),
+ ).toEqual(outputGitTag);
+ });
+ });
+ });
+
describe('mappedPullRequest', () => {
describe('mappedPullRequest happy path', () => {
it('should return PullRequest from GitPullRequest', () => {
diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
index ff63af183e..b6cc174056 100644
--- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
+++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
@@ -23,6 +23,7 @@ import {
BuildRun,
BuildStatus,
DashboardPullRequest,
+ GitTag,
Policy,
PullRequest,
PullRequestOptions,
@@ -33,6 +34,7 @@ import {
import {
GitPullRequest,
GitPullRequestSearchCriteria,
+ GitRef,
GitRepository,
} from 'azure-devops-node-api/interfaces/GitInterfaces';
import {
@@ -123,6 +125,39 @@ export class AzureDevOpsApi {
return repoBuilds;
}
+ public async getGitTags(
+ projectName: string,
+ repoName: string,
+ ): Promise {
+ this.logger?.debug(
+ `Calling Azure DevOps REST API, getting Git Tags for Repository ${repoName} for Project ${projectName}`,
+ );
+
+ const gitRepository = await this.getGitRepository(projectName, repoName);
+ const client = await this.webApi.getGitApi();
+ const tagRefs: GitRef[] = await client.getRefs(
+ gitRepository.id as string,
+ projectName,
+ 'tags',
+ false,
+ false,
+ false,
+ false,
+ true,
+ );
+ const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent(
+ projectName,
+ )}/_git/${encodeURIComponent(repoName)}?version=GT`;
+ const commitBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent(
+ projectName,
+ )}/_git/${encodeURIComponent(repoName)}/commit`;
+ const gitTags: GitTag[] = tagRefs.map(tagRef => {
+ return mappedGitTag(tagRef, linkBaseUrl, commitBaseUrl);
+ });
+
+ return gitTags;
+ }
+
public async getPullRequests(
projectName: string,
repoName: string,
@@ -359,6 +394,25 @@ export function mappedRepoBuild(build: Build): RepoBuild {
};
}
+export function mappedGitTag(
+ gitRef: GitRef,
+ linkBaseUrl: string,
+ commitBaseUrl: string,
+): GitTag {
+ return {
+ objectId: gitRef.objectId,
+ peeledObjectId: gitRef.peeledObjectId,
+ name: gitRef.name?.replace('refs/tags/', ''),
+ createdBy: gitRef.creator?.displayName ?? 'N/A',
+ link: `${linkBaseUrl}${encodeURIComponent(
+ gitRef.name?.replace('refs/tags/', '') ?? '',
+ )}`,
+ commitLink: `${commitBaseUrl}/${encodeURIComponent(
+ gitRef.peeledObjectId ?? '',
+ )}`,
+ };
+}
+
export function mappedPullRequest(
pullRequest: GitPullRequest,
linkBaseUrl: string,
diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts
index e6cd856c0a..3bf18de6a2 100644
--- a/plugins/azure-devops-backend/src/service/router.test.ts
+++ b/plugins/azure-devops-backend/src/service/router.test.ts
@@ -22,6 +22,7 @@ import {
BuildResult,
BuildRun,
BuildStatus,
+ GitTag,
PullRequest,
PullRequestStatus,
RepoBuild,
@@ -46,6 +47,7 @@ describe('createRouter', () => {
getBuildDefinitions: jest.fn(),
getRepoBuilds: jest.fn(),
getDefinitionBuilds: jest.fn(),
+ getGitTags: jest.fn(),
getPullRequests: jest.fn(),
getBuilds: jest.fn(),
getBuildRuns: jest.fn(),
@@ -208,6 +210,43 @@ describe('createRouter', () => {
});
});
+ describe('GET /git-tags/:projectName/:repoName', () => {
+ it('fetches a list of git tags', async () => {
+ const firstGitTag: GitTag = {
+ name: 'v1.1.2',
+ createdBy: 'Jane Doe',
+ commitLink:
+ 'https://host.com/myOrg/_git/super-feature-repo/commit/1234567890abcdef1234567890abcdef12345678',
+ objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee',
+ peeledObjectId: '1234567890abcdef1234567890abcdef12345678',
+ link: 'https://host.com/myOrg/_git/super-feature-repo?version=GTv1.1.2',
+ };
+
+ const secondGitTag: GitTag = {
+ name: 'v1.2.0',
+ createdBy: 'Jane Doe',
+ commitLink:
+ 'https://host.com/myOrg/_git/super-feature-repo/commit/2222222222222222222222222222222222222222',
+ objectId: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
+ peeledObjectId: '2222222222222222222222222222222222222222',
+ link: 'https://host.com/myOrg/_git/super-feature-repo?version=GTv1.2.0',
+ };
+
+ const gitTags: GitTag[] = [firstGitTag, secondGitTag];
+
+ azureDevOpsApi.getGitTags.mockResolvedValueOnce(gitTags);
+
+ const response = await request(app).get('/git-tags/myProject/myRepo');
+
+ expect(azureDevOpsApi.getGitTags).toHaveBeenCalledWith(
+ 'myProject',
+ 'myRepo',
+ );
+ expect(response.status).toEqual(200);
+ expect(response.body).toEqual(gitTags);
+ });
+ });
+
describe('GET /pull-requests/:projectName/:repoName', () => {
it('fetches a list of pull requests', async () => {
const firstPullRequest: PullRequest = {
diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts
index f41209e4ff..23ed7f146d 100644
--- a/plugins/azure-devops-backend/src/service/router.ts
+++ b/plugins/azure-devops-backend/src/service/router.ts
@@ -97,6 +97,12 @@ export async function createRouter(
res.status(200).json(gitRepository);
});
+ router.get('/git-tags/:projectName/:repoName', async (req, res) => {
+ const { projectName, repoName } = req.params;
+ const gitTags = await azureDevOpsApi.getGitTags(projectName, repoName);
+ res.status(200).json(gitTags);
+ });
+
router.get('/pull-requests/:projectName/:repoName', async (req, res) => {
const { projectName, repoName } = req.params;
diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md
index 308fd54c61..bdb4cc424d 100644
--- a/plugins/azure-devops-common/api-report.md
+++ b/plugins/azure-devops-common/api-report.md
@@ -98,6 +98,18 @@ export interface DashboardPullRequest {
title?: string;
}
+// Warning: (ae-missing-release-tag) "GitTag" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type GitTag = {
+ objectId?: string;
+ peeledObjectId?: string;
+ name?: string;
+ createdBy?: string;
+ link: string;
+ commitLink: string;
+};
+
// Warning: (ae-missing-release-tag) "Policy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts
index 7978e3326a..0ff5e897ed 100644
--- a/plugins/azure-devops-common/src/types.ts
+++ b/plugins/azure-devops-common/src/types.ts
@@ -108,6 +108,15 @@ export enum PullRequestStatus {
All = 4,
}
+export type GitTag = {
+ objectId?: string;
+ peeledObjectId?: string;
+ name?: string;
+ createdBy?: string;
+ link: string;
+ commitLink: string;
+};
+
export type PullRequest = {
pullRequestId?: number;
repoName?: string;
diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md
index a55ed983b7..68d2b57afc 100644
--- a/plugins/azure-devops/README.md
+++ b/plugins/azure-devops/README.md
@@ -16,6 +16,12 @@ Lists the top _n_ Active, Completed, or Abandoned Pull Requests for a given repo

+### Azure Repos Git Tags
+
+Lists all Git Tags for a given repository
+
+
+
## Setup
The following sections will help you get the Azure DevOps plugin setup and running
@@ -155,6 +161,42 @@ To get the Azure Repos component working you'll need to do the following two ste
- The `if` prop is optional on the `EntityLayout.Route`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation
- The `defaultLimit` property on the `EntityAzurePullRequestsContent` will set the max number of Pull Requests you would like to see, if not set this will default to 10
+### Git Tags Component
+
+To get the Git Tags component working you'll need to do the following two steps:
+
+1. First we need to add the @backstage/plugin-azure-devops package to your frontend app:
+
+ ```bash
+ # From your Backstage root directory
+ yarn add --cwd packages/app @backstage/plugin-azure-devops
+ ```
+
+2. Second we need to add the `EntityAzureGitTagsContent` extension to the entity page in your app:
+
+ ```tsx
+ // In packages/app/src/components/catalog/EntityPage.tsx
+ import {
+ EntityAzureGitTagsContent,
+ isAzureDevOpsAvailable,
+ } from '@backstage/plugin-azure-devops';
+
+ // For example in the Service section
+ const serviceEntityPage = (
+
+ // ...
+
+
+
+ // ...
+
+ ```
+
+**Notes:**
+
+- You'll need to add the `EntityLayout.Route` above from step 2 to all the entity sections you want to see Git Tags in. For example if you wanted to see Git Tags when looking at Website entities then you would need to add this to the `websiteEntityPage` section.
+- The `if` prop is optional on the `EntityLayout.Route`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation
+
## Limitations
- Currently multiple organizations are not supported
diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md
index eaba90664b..9760340771 100644
--- a/plugins/azure-devops/api-report.md
+++ b/plugins/azure-devops/api-report.md
@@ -136,6 +136,11 @@ export type CreatedByUserFilter = BaseFilter &
}
);
+// Warning: (ae-missing-release-tag) "EntityAzureGitTagsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const EntityAzureGitTagsContent: () => JSX.Element;
+
// 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)
diff --git a/plugins/azure-devops/docs/azure-devops-git-tags.png b/plugins/azure-devops/docs/azure-devops-git-tags.png
new file mode 100644
index 0000000000..a992273298
Binary files /dev/null and b/plugins/azure-devops/docs/azure-devops-git-tags.png differ
diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
index 09b48356c8..ccedc401f1 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
@@ -18,6 +18,7 @@ import {
BuildRun,
BuildRunOptions,
DashboardPullRequest,
+ GitTag,
PullRequest,
PullRequestOptions,
RepoBuild,
@@ -38,6 +39,11 @@ export interface AzureDevOpsApi {
options?: RepoBuildOptions,
): Promise<{ items: RepoBuild[] }>;
+ getGitTags(
+ projectName: string,
+ repoName: string,
+ ): Promise<{ items: GitTag[] }>;
+
getPullRequests(
projectName: string,
repoName: string,
diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
index 96c10a7b2e..61c8479d71 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
@@ -18,6 +18,7 @@ import {
BuildRun,
BuildRunOptions,
DashboardPullRequest,
+ GitTag,
PullRequest,
PullRequestOptions,
RepoBuild,
@@ -58,6 +59,18 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
return { items };
}
+ public async getGitTags(
+ projectName: string,
+ repoName: string,
+ ): Promise<{ items: GitTag[] }> {
+ const urlSegment = `git-tags/${encodeURIComponent(
+ projectName,
+ )}/${encodeURIComponent(repoName)}`;
+
+ const items = await this.get(urlSegment);
+ return { items };
+ }
+
public async getPullRequests(
projectName: string,
repoName: string,
diff --git a/plugins/azure-devops/src/components/AzureGitTagsIcon/AzureGitTagsIcon.tsx b/plugins/azure-devops/src/components/AzureGitTagsIcon/AzureGitTagsIcon.tsx
new file mode 100644
index 0000000000..c8b63e3259
--- /dev/null
+++ b/plugins/azure-devops/src/components/AzureGitTagsIcon/AzureGitTagsIcon.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 { SvgIcon, SvgIconProps } from '@material-ui/core';
+
+import React from 'react';
+
+// From https://github.com/microsoft/fluentui-system-icons/blob/30a3e3c458883a1d63a43a951407f30e6b32b60c/assets/Tag/SVG/ic_fluent_tag_32_regular.svg
+export const AzureGitTagsIcon = (props: SvgIconProps) => (
+
+
+
+);
diff --git a/plugins/azure-devops/src/components/AzureGitTagsIcon/index.ts b/plugins/azure-devops/src/components/AzureGitTagsIcon/index.ts
new file mode 100644
index 0000000000..92660244a5
--- /dev/null
+++ b/plugins/azure-devops/src/components/AzureGitTagsIcon/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { AzureGitTagsIcon } from './AzureGitTagsIcon';
diff --git a/plugins/azure-devops/src/components/EntityPageAzureGitTags/EntityPageAzureGitTags.tsx b/plugins/azure-devops/src/components/EntityPageAzureGitTags/EntityPageAzureGitTags.tsx
new file mode 100644
index 0000000000..2d94d735df
--- /dev/null
+++ b/plugins/azure-devops/src/components/EntityPageAzureGitTags/EntityPageAzureGitTags.tsx
@@ -0,0 +1,20 @@
+/*
+ * 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 { GitTagTable } from '../GitTagTable/GitTagTable';
+import React from 'react';
+
+export const EntityPageAzureGitTags = () => ;
diff --git a/plugins/azure-devops/src/components/EntityPageAzureGitTags/index.ts b/plugins/azure-devops/src/components/EntityPageAzureGitTags/index.ts
new file mode 100644
index 0000000000..b88fb6c467
--- /dev/null
+++ b/plugins/azure-devops/src/components/EntityPageAzureGitTags/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { EntityPageAzureGitTags } from './EntityPageAzureGitTags';
diff --git a/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx b/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx
new file mode 100644
index 0000000000..c6abba55e1
--- /dev/null
+++ b/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx
@@ -0,0 +1,90 @@
+/*
+ * 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 { Box } from '@material-ui/core';
+import {
+ Link,
+ ResponseErrorPanel,
+ Table,
+ TableColumn,
+} from '@backstage/core-components';
+import { GitTag } from '@backstage/plugin-azure-devops-common';
+import React from 'react';
+
+import { AzureGitTagsIcon } from '../AzureGitTagsIcon';
+import { useEntity } from '@backstage/plugin-catalog-react';
+import { useGitTags } from '../../hooks/useGitTags';
+
+const columns: TableColumn[] = [
+ {
+ title: 'Tag',
+ field: 'name',
+ highlight: false,
+ defaultSort: 'desc',
+ width: 'auto',
+ render: (row: Partial) => (
+
+ {row.name}
+
+ ),
+ },
+ {
+ title: 'Commit',
+ field: 'peeledObjectId',
+ width: 'auto',
+ render: (row: Partial) => (
+
+ {row.peeledObjectId}
+
+ ),
+ },
+ {
+ title: 'Created By',
+ field: 'createdBy',
+ width: 'auto',
+ },
+];
+
+export const GitTagTable = () => {
+ const { entity } = useEntity();
+
+ const { items, loading, error } = useGitTags(entity);
+
+ if (error) {
+ return ;
+ }
+
+ return (
+
+
+
+ Azure Repos - Git Tags ({items ? items.length : 0})
+
+ }
+ data={items ?? []}
+ />
+ );
+};
diff --git a/plugins/azure-devops/src/components/GitTagTable/index.ts b/plugins/azure-devops/src/components/GitTagTable/index.ts
new file mode 100644
index 0000000000..32933241e6
--- /dev/null
+++ b/plugins/azure-devops/src/components/GitTagTable/index.ts
@@ -0,0 +1,17 @@
+/*
+ * 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 { GitTagTable } from './GitTagTable';
diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
index 4365b796bc..b37ba6e73c 100644
--- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
+++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
@@ -103,11 +103,7 @@ export const PullRequestTable = ({ defaultLimit }: PullRequestTableProps) => {
);
if (error) {
- return (
-
-
-
- );
+ return ;
}
return (
diff --git a/plugins/azure-devops/src/hooks/useGitTags.ts b/plugins/azure-devops/src/hooks/useGitTags.ts
new file mode 100644
index 0000000000..b940a7105d
--- /dev/null
+++ b/plugins/azure-devops/src/hooks/useGitTags.ts
@@ -0,0 +1,42 @@
+/*
+ * 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 { GitTag } from '@backstage/plugin-azure-devops-common';
+
+import { Entity } from '@backstage/catalog-model';
+import { azureDevOpsApiRef } from '../api';
+import { useApi } from '@backstage/core-plugin-api';
+import useAsync from 'react-use/lib/useAsync';
+import { useProjectRepoFromEntity } from './useProjectRepoFromEntity';
+
+export function useGitTags(entity: Entity): {
+ items?: GitTag[];
+ loading: boolean;
+ error?: Error;
+} {
+ const api = useApi(azureDevOpsApiRef);
+ const { project, repo } = useProjectRepoFromEntity(entity);
+
+ const { value, loading, error } = useAsync(() => {
+ return api.getGitTags(project, repo);
+ }, [api, project, repo]);
+
+ return {
+ items: value?.items,
+ loading,
+ error,
+ };
+}
diff --git a/plugins/azure-devops/src/index.ts b/plugins/azure-devops/src/index.ts
index aa052975de..48177ee830 100644
--- a/plugins/azure-devops/src/index.ts
+++ b/plugins/azure-devops/src/index.ts
@@ -17,6 +17,7 @@
export {
azureDevOpsPlugin,
EntityAzurePipelinesContent,
+ EntityAzureGitTagsContent,
EntityAzurePullRequestsContent,
isAzureDevOpsAvailable,
isAzurePipelinesAvailable,
diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts
index c2ecb7457a..ccd183eee9 100644
--- a/plugins/azure-devops/src/plugin.ts
+++ b/plugins/azure-devops/src/plugin.ts
@@ -22,6 +22,7 @@ import {
import {
azurePipelinesEntityContentRouteRef,
azurePullRequestDashboardRouteRef,
+ azureGitTagsEntityContentRouteRef,
azurePullRequestsEntityContentRouteRef,
} from './routes';
import {
@@ -78,6 +79,17 @@ export const EntityAzurePipelinesContent = azureDevOpsPlugin.provide(
}),
);
+export const EntityAzureGitTagsContent = azureDevOpsPlugin.provide(
+ createRoutableExtension({
+ name: 'EntityAzureGitTagsContent',
+ component: () =>
+ import('./components/EntityPageAzureGitTags').then(
+ m => m.EntityPageAzureGitTags,
+ ),
+ mountPoint: azureGitTagsEntityContentRouteRef,
+ }),
+);
+
export const EntityAzurePullRequestsContent = azureDevOpsPlugin.provide(
createRoutableExtension({
name: 'EntityAzurePullRequestsContent',
diff --git a/plugins/azure-devops/src/routes.ts b/plugins/azure-devops/src/routes.ts
index 0d20c12beb..4ed35bbe12 100644
--- a/plugins/azure-devops/src/routes.ts
+++ b/plugins/azure-devops/src/routes.ts
@@ -24,6 +24,10 @@ export const azurePipelinesEntityContentRouteRef = createRouteRef({
id: 'azure-pipelines-entity-content',
});
+export const azureGitTagsEntityContentRouteRef = createRouteRef({
+ id: 'azure-git-tags-entity-content',
+});
+
export const azurePullRequestsEntityContentRouteRef = createRouteRef({
id: 'azure-pull-requests-entity-content',
});