diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json
index 23a575f95e..70b7be4964 100644
--- a/plugins/azure-devops-common/package.json
+++ b/plugins/azure-devops-common/package.json
@@ -36,5 +36,9 @@
},
"files": [
"dist"
- ]
+ ],
+ "dependencies": {
+ "@backstage/plugin-catalog-common": "workspace:^",
+ "@backstage/plugin-permission-common": "workspace:^"
+ }
}
diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
index 89aa4b7d6e..6dd6eeae6b 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
@@ -48,6 +48,7 @@ export interface AzureDevOpsApi {
getGitTags(
projectName: string,
repoName: string,
+ entityRef: string,
host?: string,
org?: string,
): Promise<{ items: GitTag[] }>;
@@ -55,6 +56,7 @@ export interface AzureDevOpsApi {
getPullRequests(
projectName: string,
repoName: string,
+ entityRef: string,
host?: string,
org?: string,
options?: PullRequestOptions,
@@ -70,6 +72,7 @@ export interface AzureDevOpsApi {
getBuildRuns(
projectName: string,
+ entityRef: string,
repoName?: string,
definitionName?: string,
host?: string,
diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
index f5c046ef07..257b1cd014 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
@@ -71,6 +71,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
public async getGitTags(
projectName: string,
+ entityRef: string,
repoName: string,
host?: string,
org?: string,
@@ -82,6 +83,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
if (org) {
queryString.append('org', org);
}
+ queryString.append('entityRef', entityRef);
const urlSegment = `git-tags/${encodeURIComponent(
projectName,
)}/${encodeURIComponent(repoName)}?${queryString}`;
@@ -93,6 +95,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
public async getPullRequests(
projectName: string,
repoName: string,
+ entityRef: string,
host?: string,
org?: string,
options?: PullRequestOptions,
@@ -110,6 +113,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
if (org) {
queryString.append('org', org);
}
+ queryString.append('entityRef', entityRef);
const urlSegment = `pull-requests/${encodeURIComponent(
projectName,
)}/${encodeURIComponent(repoName)}?${queryString}`;
@@ -136,6 +140,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
public async getBuildRuns(
projectName: string,
+ entityRef: string,
repoName?: string,
definitionName?: string,
host?: string,
@@ -174,6 +179,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
if (options?.top) {
queryString.append('top', options.top.toString());
}
+ queryString.append('entityRef', entityRef);
const urlSegment = `builds/${encodeURIComponent(
projectName,
)}?${queryString}`;
diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
index 09eba5f5a2..bc69b68d56 100644
--- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
+++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
@@ -32,12 +32,14 @@ import {
StatusWarning,
Table,
TableColumn,
+ EmptyState,
} from '@backstage/core-components';
import { AzurePipelinesIcon } from '../AzurePipelinesIcon';
import { DateTime } from 'luxon';
import React from 'react';
import { getDurationFromDates } from '../../utils/getDurationFromDates';
+import { isAuthorizationError } from '../../utils';
export const getBuildResultComponent = (result: number | undefined) => {
switch (result) {
@@ -179,6 +181,15 @@ type BuildTableProps = {
export const BuildTable = ({ items, loading, error }: BuildTableProps) => {
if (error) {
+ if (isAuthorizationError(error)) {
+ return (
+
+ );
+ }
return (
diff --git a/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx b/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx
index f34a1929fe..31475a66d7 100644
--- a/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx
+++ b/plugins/azure-devops/src/components/GitTagTable/GitTagTable.tsx
@@ -20,6 +20,7 @@ import {
ResponseErrorPanel,
Table,
TableColumn,
+ EmptyState,
} from '@backstage/core-components';
import { GitTag } from '@backstage/plugin-azure-devops-common';
import React from 'react';
@@ -27,6 +28,7 @@ import React from 'react';
import { AzureGitTagsIcon } from '../AzureGitTagsIcon';
import { useEntity } from '@backstage/plugin-catalog-react';
import { useGitTags } from '../../hooks/useGitTags';
+import { isAuthorizationError } from '../../utils';
const columns: TableColumn[] = [
{
@@ -64,6 +66,15 @@ export const GitTagTable = () => {
const { items, loading, error } = useGitTags(entity);
if (error) {
+ if (isAuthorizationError(error)) {
+ return (
+
+ );
+ }
return ;
}
diff --git a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
index d672102945..3e41a8c46f 100644
--- a/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
+++ b/plugins/azure-devops/src/components/PullRequestTable/PullRequestTable.tsx
@@ -21,6 +21,7 @@ import {
ResponseErrorPanel,
Table,
TableColumn,
+ EmptyState,
} from '@backstage/core-components';
import {
PullRequest,
@@ -33,6 +34,7 @@ import { DateTime } from 'luxon';
import { PullRequestStatusButtonGroup } from '../PullRequestStatusButtonGroup';
import { useEntity } from '@backstage/plugin-catalog-react';
import { usePullRequests } from '../../hooks/usePullRequests';
+import { isAuthorizationError } from '../../utils';
const columns: TableColumn[] = [
{
@@ -104,6 +106,15 @@ export const PullRequestTable = ({ defaultLimit }: PullRequestTableProps) => {
);
if (error) {
+ if (isAuthorizationError(error)) {
+ return (
+
+ );
+ }
return ;
}
diff --git a/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx b/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx
index c5da207bd6..9561c76a2c 100644
--- a/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx
+++ b/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx
@@ -16,6 +16,7 @@
import {
Content,
+ EmptyState,
Header,
Page,
Progress,
@@ -28,6 +29,7 @@ import { FilterType } from './lib/filters';
import { PullRequestGrid } from './lib/PullRequestGrid';
import { useDashboardPullRequests } from '../../hooks';
import { useFilterProcessor } from './lib/hooks';
+import { isAuthorizationError } from '../../utils';
type PullRequestsPageContentProps = {
pullRequestGroups: PullRequestGroup[] | undefined;
@@ -45,6 +47,15 @@ const PullRequestsPageContent = ({
}
if (error) {
+ if (isAuthorizationError(error)) {
+ return (
+
+ );
+ }
return ;
}
diff --git a/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx b/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx
index 300c9e0d7c..a45c6fe831 100644
--- a/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx
+++ b/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx
@@ -28,6 +28,7 @@ import { useEntity } from '@backstage/plugin-catalog-react';
import React from 'react';
import { useReadme } from '../../hooks';
+import { isAuthorizationError } from '../../utils';
const useStyles = makeStyles(theme => ({
readMe: {
@@ -63,7 +64,15 @@ function isNotFoundError(error: any): boolean {
}
const ReadmeCardError = ({ error }: ErrorProps) => {
- if (isNotFoundError(error))
+ if (isAuthorizationError(error)) {
+ return (
+
+ );
+ } else if (isNotFoundError(error)) {
return (
{
}
/>
);
+ }
return ;
};
diff --git a/plugins/azure-devops/src/hooks/useBuildRuns.ts b/plugins/azure-devops/src/hooks/useBuildRuns.ts
index e335d2ffaf..21f4d60430 100644
--- a/plugins/azure-devops/src/hooks/useBuildRuns.ts
+++ b/plugins/azure-devops/src/hooks/useBuildRuns.ts
@@ -24,7 +24,7 @@ import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { getAnnotationValuesFromEntity } from '../utils';
-import { Entity } from '@backstage/catalog-model';
+import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
export function useBuildRuns(
entity: Entity,
@@ -44,7 +44,15 @@ export function useBuildRuns(
const { value, loading, error } = useAsync(() => {
const { project, repo, definition, host, org } =
getAnnotationValuesFromEntity(entity);
- return api.getBuildRuns(project, repo, definition, host, org, options);
+ return api.getBuildRuns(
+ project,
+ stringifyEntityRef(entity),
+ repo,
+ definition,
+ host,
+ org,
+ options,
+ );
}, [api]);
return {
diff --git a/plugins/azure-devops/src/hooks/useGitTags.ts b/plugins/azure-devops/src/hooks/useGitTags.ts
index 57489316da..3cd65dad92 100644
--- a/plugins/azure-devops/src/hooks/useGitTags.ts
+++ b/plugins/azure-devops/src/hooks/useGitTags.ts
@@ -16,7 +16,7 @@
import { GitTag } from '@backstage/plugin-azure-devops-common';
-import { Entity } from '@backstage/catalog-model';
+import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
@@ -31,7 +31,13 @@ export function useGitTags(entity: Entity): {
const { value, loading, error } = useAsync(() => {
const { project, repo, host, org } = getAnnotationValuesFromEntity(entity);
- return api.getGitTags(project, repo as string, host, org);
+ return api.getGitTags(
+ project,
+ repo as string,
+ stringifyEntityRef(entity),
+ host,
+ org,
+ );
}, [api]);
return {
diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts
index af2c4b1cc1..7347f66156 100644
--- a/plugins/azure-devops/src/hooks/usePullRequests.ts
+++ b/plugins/azure-devops/src/hooks/usePullRequests.ts
@@ -21,7 +21,7 @@ import {
PullRequestStatus,
} from '@backstage/plugin-azure-devops-common';
-import { Entity } from '@backstage/catalog-model';
+import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
@@ -47,7 +47,15 @@ export function usePullRequests(
const { value, loading, error } = useAsync(() => {
const { project, repo, host, org } = getAnnotationValuesFromEntity(entity);
- return api.getPullRequests(project, repo as string, host, org, options);
+ const entityRef = stringifyEntityRef(entity);
+ return api.getPullRequests(
+ project,
+ repo as string,
+ entityRef,
+ host,
+ org,
+ options,
+ );
}, [api, top, status]);
return {
diff --git a/plugins/azure-devops/src/hooks/useReadme.ts b/plugins/azure-devops/src/hooks/useReadme.ts
index 9aa376e0bd..cfc7f04bf4 100644
--- a/plugins/azure-devops/src/hooks/useReadme.ts
+++ b/plugins/azure-devops/src/hooks/useReadme.ts
@@ -16,7 +16,7 @@
import { Readme } from '@backstage/plugin-azure-devops-common';
-import { Entity } from '@backstage/catalog-model';
+import { Entity, stringifyEntityRef } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
@@ -32,9 +32,11 @@ export function useReadme(entity: Entity): {
const { value, loading, error } = useAsync(() => {
const { project, repo, host, org, readmePath } =
getAnnotationValuesFromEntity(entity);
+ const entityRef = stringifyEntityRef(entity);
return api.getReadme({
project,
repo: repo as string,
+ entityRef,
host,
org,
path: readmePath,
diff --git a/plugins/azure-devops/src/utils/index.ts b/plugins/azure-devops/src/utils/index.ts
index 998bb0e23d..a05ea4f650 100644
--- a/plugins/azure-devops/src/utils/index.ts
+++ b/plugins/azure-devops/src/utils/index.ts
@@ -18,3 +18,4 @@ export * from './arrayHas';
export * from './equalsIgnoreCase';
export * from './getDurationFromDates';
export * from './getAnnotationValuesFromEntity';
+export * from './validateError';
diff --git a/plugins/azure-devops/src/utils/validateError.ts b/plugins/azure-devops/src/utils/validateError.ts
new file mode 100644
index 0000000000..fca1687013
--- /dev/null
+++ b/plugins/azure-devops/src/utils/validateError.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2024 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 function isAuthorizationError(error: any): boolean {
+ return error?.response?.status === 403;
+}