diff --git a/.changeset/beige-apples-press.md b/.changeset/beige-apples-press.md
new file mode 100644
index 0000000000..56c46db830
--- /dev/null
+++ b/.changeset/beige-apples-press.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-azure-devops': patch
+---
+
+Added alpha support for the New Frontend System (Declarative Integration)
diff --git a/.changeset/calm-onions-exercise.md b/.changeset/calm-onions-exercise.md
new file mode 100644
index 0000000000..f1c58a173f
--- /dev/null
+++ b/.changeset/calm-onions-exercise.md
@@ -0,0 +1,5 @@
+---
+'@backstage/repo-tools': patch
+---
+
+Resolved an issue with generate-catalog-info where it was replacing upper case characters with -.
diff --git a/.changeset/fast-onions-own.md b/.changeset/fast-onions-own.md
new file mode 100644
index 0000000000..6977b035bc
--- /dev/null
+++ b/.changeset/fast-onions-own.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-common': patch
+---
+
+`FetchUrlReader#readUrl()` now supports passing an optional `token` to authenticate requests.
diff --git a/.changeset/shiny-clocks-greet.md b/.changeset/shiny-clocks-greet.md
new file mode 100644
index 0000000000..0c112ff81f
--- /dev/null
+++ b/.changeset/shiny-clocks-greet.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-user-settings': patch
+---
+
+conditionally rendering the user name and email in user settings page
diff --git a/.changeset/strong-news-develop.md b/.changeset/strong-news-develop.md
new file mode 100644
index 0000000000..1cbe4dda07
--- /dev/null
+++ b/.changeset/strong-news-develop.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-explore': patch
+---
+
+Use the EntityDisplayName component for rendering Group nodes
diff --git a/.changeset/tall-frogs-clap.md b/.changeset/tall-frogs-clap.md
new file mode 100644
index 0000000000..1e87ca316c
--- /dev/null
+++ b/.changeset/tall-frogs-clap.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-azure-sites-backend': patch
+---
+
+Added `permissionIntegrationRouter` for azure-sites-backend routes
diff --git a/.changeset/wise-papayas-cough.md b/.changeset/wise-papayas-cough.md
new file mode 100644
index 0000000000..955e7fefcc
--- /dev/null
+++ b/.changeset/wise-papayas-cough.md
@@ -0,0 +1,5 @@
+---
+'@backstage/backend-common': patch
+---
+
+Add a config declaration for `workingDirectory`
diff --git a/.husky/pre-commit b/.husky/pre-commit
index d2ae35e84b..3723623171 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,4 +1 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
yarn lint-staged
diff --git a/microsite/data/plugins/umami.yaml b/microsite/data/plugins/umami.yaml
new file mode 100644
index 0000000000..5f1fc208d4
--- /dev/null
+++ b/microsite/data/plugins/umami.yaml
@@ -0,0 +1,10 @@
+---
+title: Umami Analytics
+author: AxisCommunications
+authorUrl: https://github.com/AxisCommunications
+category: Monitoring
+description: Track usage of your Backstage instance using Umami Analytics.
+documentation: https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/analytics-module-umami/README.md
+iconUrl: /img/umami-logo.svg
+npmPackageName: '@axis-backstage/plugin-analytics-module-umami'
+addedDate: '2024-02-05'
diff --git a/microsite/static/img/umami-logo.svg b/microsite/static/img/umami-logo.svg
new file mode 100644
index 0000000000..b139531324
--- /dev/null
+++ b/microsite/static/img/umami-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 6deae7611e..fbe47ec8b4 100644
--- a/package.json
+++ b/package.json
@@ -40,8 +40,8 @@
"build-storybook": "yarn ./storybook run build-storybook",
"techdocs-cli": "node scripts/techdocs-cli.js",
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
- "prepare": "husky install",
- "postinstall": "husky install || true"
+ "prepare": "husky",
+ "postinstall": "husky || true"
},
"workspaces": {
"packages": [
@@ -85,7 +85,7 @@
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-testing-library": "^6.0.0",
"fs-extra": "10.1.0",
- "husky": "^8.0.0",
+ "husky": "^9.0.0",
"lint-staged": "^15.0.0",
"minimist": "^1.2.5",
"node-gyp": "^10.0.0",
diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml
index 2007aaba58..0dd2187418 100644
--- a/packages/app-next/app-config.yaml
+++ b/packages/app-next/app-config.yaml
@@ -19,8 +19,12 @@ app:
- entity-card:catalog-graph/relations:
config:
height: 300
+ - entity-card:azure-devops/readme
# Entity page content
- entity-content:techdocs
+ - entity-content:azure-devops/pipelines
+ - entity-content:azure-devops/pull-requests
+ - entity-content:azure-devops/git-tags
# scmAuthExtension: >-
# createScmAuthExtension({
diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts
index 8bcc5465d5..700d9a6c6c 100644
--- a/packages/backend-common/config.d.ts
+++ b/packages/backend-common/config.d.ts
@@ -67,6 +67,22 @@ export interface Config {
};
};
+ /**
+ * An absolute path to a directory that can be used as a working dir, for
+ * example as scratch space for large operations.
+ *
+ * @remarks
+ *
+ * Note that this must be an absolute path.
+ *
+ * If not set, the operating system's designated temporary directory is
+ * commonly used, but that is implementation defined per plugin.
+ *
+ * Plugins are encouraged to heed this config setting if present, to allow
+ * deployment in severely locked-down or limited environments.
+ */
+ workingDirectory?: string;
+
/** Database connection configuration, select base database type using the `client` field */
database: {
/** Default database client to use */
diff --git a/packages/backend-common/src/reading/FetchUrlReader.test.ts b/packages/backend-common/src/reading/FetchUrlReader.test.ts
index ad834c64ef..7b5e4ebc05 100644
--- a/packages/backend-common/src/reading/FetchUrlReader.test.ts
+++ b/packages/backend-common/src/reading/FetchUrlReader.test.ts
@@ -223,6 +223,27 @@ describe('FetchUrlReader', () => {
).rejects.toThrow(NotModifiedError);
});
+ it('should send Authorization header if token is provided', async () => {
+ expect.assertions(1);
+
+ worker.use(
+ rest.get(
+ 'https://backstage.io/requires-authentication',
+ (req, res, ctx) => {
+ expect(req.headers.get('authorization')).toBe('Bearer mytoken');
+ return res(ctx.status(200));
+ },
+ ),
+ );
+
+ await fetchUrlReader.readUrl(
+ 'https://backstage.io/requires-authentication',
+ {
+ token: 'mytoken',
+ },
+ );
+ });
+
it('should return etag from the response', async () => {
const response = await fetchUrlReader.readUrl(
'https://backstage.io/some-resource',
diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts
index 90c1cf6c26..475a98ffdc 100644
--- a/packages/backend-common/src/reading/FetchUrlReader.ts
+++ b/packages/backend-common/src/reading/FetchUrlReader.ts
@@ -131,6 +131,7 @@ export class FetchUrlReader implements UrlReader {
...(options?.lastModifiedAfter && {
'If-Modified-Since': options.lastModifiedAfter.toUTCString(),
}),
+ ...(options?.token && { Authorization: `Bearer ${options.token}` }),
},
// TODO(freben): The signal cast is there because pre-3.x versions of
// node-fetch have a very slightly deviating AbortSignal type signature.
diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json
index dff05e9774..51d4bcdf4c 100644
--- a/packages/core-compat-api/package.json
+++ b/packages/core-compat-api/package.json
@@ -12,6 +12,11 @@
"backstage": {
"role": "web-library"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/backstage/backstage",
+ "directory": "packages/core-compat-api"
+ },
"sideEffects": false,
"scripts": {
"start": "backstage-cli package start",
diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json
index 7d8009f338..7d5b99d504 100644
--- a/packages/frontend-plugin-api/package.json
+++ b/packages/frontend-plugin-api/package.json
@@ -12,6 +12,11 @@
"backstage": {
"role": "web-library"
},
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/backstage/backstage",
+ "directory": "packages/frontend-plugin-api"
+ },
"sideEffects": false,
"scripts": {
"start": "backstage-cli package start",
diff --git a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts
index b8a473c67b..1893bd25c8 100644
--- a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts
+++ b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts
@@ -33,6 +33,7 @@ import {
isFulfilled,
readFile,
writeFile,
+ safeEntityName,
} from './utils';
import { CodeOwnersEntry } from 'codeowners-utils';
@@ -164,9 +165,7 @@ async function fixCatalogInfoYaml(options: FixOptions) {
codeowners,
relativePath('.', yamlPath),
);
- const safeName = packageJson.name
- .replace(/[^a-z0-9_\-\.]+/g, '-')
- .replace(/^[^a-z0-9]|[^a-z0-9]$/g, '');
+ const safeName = safeEntityName(packageJson.name);
let yamlJson: BackstagePackageEntity;
try {
@@ -240,9 +239,7 @@ function createOrMergeEntity(
owner: string,
existingEntity: BackstagePackageEntity | Record = {},
): BackstagePackageEntity {
- const safeEntityName = packageJson.name
- .replace(/[^a-z0-9_\-\.]+/g, '-')
- .replace(/^[^a-z0-9]|[^a-z0-9]$/g, '');
+ const entityName = safeEntityName(packageJson.name);
return {
...existingEntity,
@@ -251,7 +248,7 @@ function createOrMergeEntity(
metadata: {
...existingEntity.metadata,
// Provide default name/title/description values.
- name: safeEntityName,
+ name: entityName,
title: packageJson.name,
...(packageJson.description && !existingEntity.metadata?.description
? { description: packageJson.description }
diff --git a/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts b/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts
new file mode 100644
index 0000000000..40c1bb1e19
--- /dev/null
+++ b/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+import { safeEntityName } from './utils';
+
+describe('utils', () => {
+ describe('safeEntityName', () => {
+ it('should remove non-alphanumeric characters at the start and end', () => {
+ const result = safeEntityName('%entityname$');
+ expect(result).toBe('entityname');
+ });
+
+ it('should replace non-alphanumeric characters, except - and _, with -', () => {
+ const result = safeEntityName('entity@#name$');
+ expect(result).toBe('entity-name');
+ });
+
+ it('should replace capital letters with - followed by the same letter in lowercase', () => {
+ const result = safeEntityName('EntityName');
+ expect(result).toBe('entity-name');
+ });
+ });
+});
diff --git a/packages/repo-tools/src/commands/generate-catalog-info/utils.ts b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts
index b00fbd57a8..3f23b35112 100644
--- a/packages/repo-tools/src/commands/generate-catalog-info/utils.ts
+++ b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts
@@ -48,3 +48,20 @@ export const isRejected = (
export const isFulfilled = (
input: PromiseSettledResult,
): input is PromiseFulfilledResult => input.status === 'fulfilled';
+
+/**
+ * Generates a suitable entity name from a package name by slugifying the given package name.
+ *
+ * @param packageName - The package name to generate an entity name from.
+ * @returns The generated entity name, a slugified version of the package name.
+ */
+export const safeEntityName = (packageName: string): string => {
+ return packageName
+ .replace(/^[^\w\s]|[^a-z0-9]$/g, '')
+ .replace(/[^A-Za-z0-9_\-.]+/g, '-')
+ .replace(
+ /([a-z])([A-Z])/g,
+ (_, a, b) => `${a}-${b.toLocaleLowerCase('en-US')}`,
+ )
+ .replace(/^(.)/, (_, a) => a.toLocaleLowerCase('en-US'));
+};
diff --git a/plugins/azure-devops/api-report-alpha.md b/plugins/azure-devops/api-report-alpha.md
new file mode 100644
index 0000000000..8040c0235f
--- /dev/null
+++ b/plugins/azure-devops/api-report-alpha.md
@@ -0,0 +1,13 @@
+## API Report File for "@backstage/plugin-azure-devops"
+
+> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
+
+```ts
+import { BackstagePlugin } from '@backstage/frontend-plugin-api';
+
+// @alpha (undocumented)
+const _default: BackstagePlugin<{}, {}>;
+export default _default;
+
+// (No @packageDocumentation comment for this package)
+```
diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json
index e0cf441155..84024646dd 100644
--- a/plugins/azure-devops/package.json
+++ b/plugins/azure-devops/package.json
@@ -5,9 +5,22 @@
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
- "access": "public",
- "main": "dist/index.esm.js",
- "types": "dist/index.d.ts"
+ "access": "public"
+ },
+ "exports": {
+ ".": "./src/index.ts",
+ "./alpha": "./src/alpha.ts",
+ "./package.json": "./package.json"
+ },
+ "typesVersions": {
+ "*": {
+ "alpha": [
+ "src/alpha.ts"
+ ],
+ "package.json": [
+ "package.json"
+ ]
+ }
},
"backstage": {
"role": "frontend-plugin"
@@ -30,9 +43,11 @@
},
"dependencies": {
"@backstage/catalog-model": "workspace:^",
+ "@backstage/core-compat-api": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
+ "@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-azure-devops-common": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@material-ui/core": "^4.12.2",
diff --git a/plugins/azure-devops/src/alpha.ts b/plugins/azure-devops/src/alpha.ts
new file mode 100644
index 0000000000..e80f131817
--- /dev/null
+++ b/plugins/azure-devops/src/alpha.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2023 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 './alpha/index';
+export { default } from './alpha/index';
diff --git a/plugins/azure-devops/src/alpha/index.ts b/plugins/azure-devops/src/alpha/index.ts
new file mode 100644
index 0000000000..2f137f09ee
--- /dev/null
+++ b/plugins/azure-devops/src/alpha/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2023 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 { default } from './plugin';
diff --git a/plugins/azure-devops/src/alpha/plugin.tsx b/plugins/azure-devops/src/alpha/plugin.tsx
new file mode 100644
index 0000000000..40305c1714
--- /dev/null
+++ b/plugins/azure-devops/src/alpha/plugin.tsx
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 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 {
+ createApiExtension,
+ createApiFactory,
+ createPageExtension,
+ createPlugin,
+ discoveryApiRef,
+ identityApiRef,
+} from '@backstage/frontend-plugin-api';
+import { azureDevOpsApiRef, AzureDevOpsClient } from '../api';
+import {
+ compatWrapper,
+ convertLegacyRouteRef,
+} from '@backstage/core-compat-api';
+import {
+ createEntityCardExtension,
+ createEntityContentExtension,
+} from '@backstage/plugin-catalog-react/alpha';
+import { azurePullRequestDashboardRouteRef } from '../routes';
+
+/** @alpha */
+export const azureDevOpsApi = createApiExtension({
+ factory: createApiFactory({
+ api: azureDevOpsApiRef,
+ deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
+ factory: ({ discoveryApi, identityApi }) =>
+ new AzureDevOpsClient({ discoveryApi, identityApi }),
+ }),
+});
+
+/** @alpha */
+export const azureDevOpsPullRequestPage = createPageExtension({
+ defaultPath: '/azure-pull-requests',
+ routeRef: convertLegacyRouteRef(azurePullRequestDashboardRouteRef),
+ loader: () =>
+ import('../components/PullRequestsPage').then(m =>
+ compatWrapper(),
+ ),
+});
+
+/** @alpha */
+export const azureDevOpsPipelinesEntityContent = createEntityContentExtension({
+ name: 'pipelines',
+ defaultPath: '/pipelines',
+ defaultTitle: 'Pipelines',
+ loader: () =>
+ import('../components/EntityPageAzurePipelines').then(m =>
+ compatWrapper(),
+ ),
+});
+
+/** @alpha */
+export const azureDevOpsGitTagsEntityContent = createEntityContentExtension({
+ name: 'git-tags',
+ defaultPath: '/git-tags',
+ defaultTitle: 'Git Tags',
+ loader: () =>
+ import('../components/EntityPageAzureGitTags').then(m =>
+ compatWrapper(),
+ ),
+});
+
+/** @alpha */
+export const azureDevOpsPullRequestsEntityContent =
+ createEntityContentExtension({
+ name: 'pull-requests',
+ defaultPath: '/pull-requests',
+ defaultTitle: 'Pull Requests',
+ loader: () =>
+ import('../components/EntityPageAzurePullRequests').then(m =>
+ compatWrapper(),
+ ),
+ });
+
+/** @alpha */
+export const azureDevOpsReadmeEntityCard = createEntityCardExtension({
+ name: 'readme',
+ loader: async () =>
+ import('../components/ReadmeCard').then(m =>
+ compatWrapper(),
+ ),
+});
+
+/** @alpha */
+export default createPlugin({
+ id: 'azure-devops',
+ extensions: [
+ azureDevOpsApi,
+ azureDevOpsReadmeEntityCard,
+ azureDevOpsPipelinesEntityContent,
+ azureDevOpsGitTagsEntityContent,
+ azureDevOpsPullRequestsEntityContent,
+ azureDevOpsPullRequestPage,
+ ],
+});
diff --git a/plugins/azure-sites-backend/src/service/router.ts b/plugins/azure-sites-backend/src/service/router.ts
index 9ed198cc20..bd5d20c05e 100644
--- a/plugins/azure-sites-backend/src/service/router.ts
+++ b/plugins/azure-sites-backend/src/service/router.ts
@@ -27,8 +27,10 @@ import {
} from '@backstage/plugin-permission-common';
import {
azureSitesActionPermission,
+ azureSitesPermissions,
AZURE_WEB_SITE_NAME_ANNOTATION,
} from '@backstage/plugin-azure-sites-common';
+import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
import { CatalogApi } from '@backstage/catalog-client';
import { AzureSitesApi } from '../api';
@@ -47,8 +49,13 @@ export async function createRouter(
): Promise {
const { logger, azureSitesApi, permissions, catalogApi } = options;
+ const permissionIntegrationRouter = createPermissionIntegrationRouter({
+ permissions: azureSitesPermissions,
+ });
+
const router = Router();
router.use(express.json());
+ router.use(permissionIntegrationRouter);
router.get('/health', (_, response) => {
logger.info('PONG!');
diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx
index 7c57de84bc..1329d3c522 100644
--- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx
+++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.test.tsx
@@ -21,6 +21,7 @@ import {
} from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
+import { screen } from '@testing-library/react';
import React from 'react';
import { GroupsDiagram } from './GroupsDiagram';
@@ -55,7 +56,7 @@ describe('', () => {
}),
};
- const { getByText } = await renderInTestApp(
+ await renderInTestApp(
,
@@ -65,6 +66,9 @@ describe('', () => {
},
},
);
- expect(getByText('Group A', { selector: 'div' })).toBeInTheDocument();
+
+ expect(
+ screen.getByRole('link', { name: 'group:my-namespace/group-a' }),
+ ).toBeInTheDocument();
});
});
diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx
index f7f0eab0bd..dd0705908a 100644
--- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx
+++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx
@@ -15,7 +15,6 @@
*/
import {
- GroupEntity,
parseEntityRef,
RELATION_CHILD_OF,
stringifyEntityRef,
@@ -31,8 +30,8 @@ import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
import {
catalogApiRef,
entityRouteRef,
- humanizeEntityRef,
getEntityRelations,
+ EntityDisplayName,
} from '@backstage/plugin-catalog-react';
import { makeStyles, Typography, useTheme } from '@material-ui/core';
import ZoomOutMap from '@material-ui/icons/ZoomOutMap';
@@ -140,7 +139,9 @@ function RenderNode(props: DependencyGraphTypes.RenderNodeProps) {
rx={theme.shape.borderRadius}
className={classes.groupNode}
/>
- {props.node.name}
+
+
+
) {
@@ -210,9 +211,7 @@ export function GroupsDiagram(props: {
nodes.push({
id: stringifyEntityRef(catalogItem),
kind: catalogItem.kind,
- name:
- (catalogItem as GroupEntity).spec?.profile?.displayName ||
- humanizeEntityRef(catalogItem, { defaultKind: 'Group' }),
+ name: '',
});
// Edge to parent
diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx
index 9b461fba7d..00a6164aa9 100644
--- a/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx
+++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx
@@ -17,7 +17,7 @@
import { Entity } from '@backstage/catalog-model';
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
-import { waitFor } from '@testing-library/react';
+import { waitFor, screen } from '@testing-library/react';
import React from 'react';
import { GroupsExplorerContent } from '../GroupsExplorerContent';
@@ -73,24 +73,24 @@ describe('', () => {
];
catalogApi.getEntities.mockResolvedValue({ items: entities });
- const { getByText } = await renderInTestApp(
+ await renderInTestApp(
,
mountedRoutes,
);
- await waitFor(() => {
+ await waitFor(() =>
expect(
- getByText('my-namespace/group-a', { selector: 'div' }),
- ).toBeInTheDocument();
- });
+ screen.getByRole('link', { name: 'group:my-namespace/group-a' }),
+ ).toBeInTheDocument(),
+ );
});
it('renders a custom title', async () => {
catalogApi.getEntities.mockResolvedValue({ items: [] });
- const { getByText } = await renderInTestApp(
+ await renderInTestApp(
,
@@ -98,7 +98,9 @@ describe('', () => {
);
await waitFor(() =>
- expect(getByText('Our Teams', { selector: 'h2' })).toBeInTheDocument(),
+ expect(
+ screen.getByText('Our Teams', { selector: 'h2' }),
+ ).toBeInTheDocument(),
);
});
@@ -106,7 +108,7 @@ describe('', () => {
const catalogError = new Error('Network timeout');
catalogApi.getEntities.mockRejectedValueOnce(catalogError);
- const { getAllByText } = await renderInTestApp(
+ await renderInTestApp(
,
@@ -114,7 +116,7 @@ describe('', () => {
);
await waitFor(() =>
- expect(getAllByText(/Error: Network timeout/).length).not.toBe(0),
+ expect(screen.getAllByText(/Error: Network timeout/).length).not.toBe(0),
);
});
});
diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx
index 9ff1dd1b17..ec4e4e1b0a 100644
--- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx
+++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx
@@ -358,12 +358,11 @@ describe('TemplateCard', () => {
},
);
- expect(
- getByRole('link', { name: 'group:default/my-test-user' }),
- ).toBeInTheDocument();
- expect(
- getByRole('link', { name: 'group:default/my-test-user' }),
- ).toHaveAttribute('href', '/catalog/group/default/my-test-user');
+ expect(getByRole('link', { name: /.*my-test-user$/ })).toBeInTheDocument();
+ expect(getByRole('link', { name: /.*my-test-user$/ })).toHaveAttribute(
+ 'href',
+ '/catalog/group/default/my-test-user',
+ );
});
it('should call the onSelected handler when clicking the choose button', async () => {
diff --git a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
index e3fb597a15..e9a710036f 100644
--- a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
+++ b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx
@@ -101,16 +101,20 @@ export const ProviderSettingsItem = (props: {
-
- {profile.displayName}
-
-
- {profile.email}
-
+ {profile.displayName && (
+
+ {profile.displayName}
+
+ )}
+ {profile.email && (
+
+ {profile.email}
+
+ )}
{description}
diff --git a/yarn.lock b/yarn.lock
index 5cd5a0cbf5..df56bc2fbd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5052,10 +5052,12 @@ __metadata:
dependencies:
"@backstage/catalog-model": "workspace:^"
"@backstage/cli": "workspace:^"
+ "@backstage/core-compat-api": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/dev-utils": "workspace:^"
"@backstage/errors": "workspace:^"
+ "@backstage/frontend-plugin-api": "workspace:^"
"@backstage/plugin-azure-devops-common": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
@@ -29841,12 +29843,12 @@ __metadata:
languageName: node
linkType: hard
-"husky@npm:^8.0.0":
- version: 8.0.3
- resolution: "husky@npm:8.0.3"
+"husky@npm:^9.0.0":
+ version: 9.0.10
+ resolution: "husky@npm:9.0.10"
bin:
- husky: lib/bin.js
- checksum: 837bc7e4413e58c1f2946d38fb050f5d7324c6f16b0fd66411ffce5703b294bd21429e8ba58711cd331951ee86ed529c5be4f76805959ff668a337dbfa82a1b0
+ husky: bin.mjs
+ checksum: 55f4b6db6706ff0bc181607d6a64f55310cbb18b4d7db2a5b85608c87a80abafc14ac3a8c4d0b44d6272f4a62d4c2d3d5491d6a13e2cc7ac4895309e5e5f0ec2
languageName: node
linkType: hard
@@ -40405,7 +40407,7 @@ __metadata:
eslint-plugin-react: ^7.28.0
eslint-plugin-testing-library: ^6.0.0
fs-extra: 10.1.0
- husky: ^8.0.0
+ husky: ^9.0.0
lint-staged: ^15.0.0
minimist: ^1.2.5
node-gyp: ^10.0.0