diff --git a/.changeset/clean-fans-change.md b/.changeset/clean-fans-change.md
new file mode 100644
index 0000000000..fad4a9cfa6
--- /dev/null
+++ b/.changeset/clean-fans-change.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-kubernetes': patch
+---
+
+fix: kubernetes plugin shall pass id token on get clusters request if possible
diff --git a/.changeset/early-trains-hammer.md b/.changeset/early-trains-hammer.md
new file mode 100644
index 0000000000..853788e015
--- /dev/null
+++ b/.changeset/early-trains-hammer.md
@@ -0,0 +1,7 @@
+---
+'@backstage/plugin-catalog-backend-module-msgraph': patch
+---
+
+Tweak logic for msgraph catalog ingesting for display names with security groups
+
+Previously security groups that weren't mail enabled were imported with UUIDs, now they use the display name.
diff --git a/.changeset/four-years-develop.md b/.changeset/four-years-develop.md
new file mode 100644
index 0000000000..cfd3704f2f
--- /dev/null
+++ b/.changeset/four-years-develop.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': patch
+---
+
+Allow for `cellStyle` property on `TableColumn` to be a function as well as `React.CSSProperties` as per the Material UI Table component
diff --git a/.changeset/many-mayflies-notice.md b/.changeset/many-mayflies-notice.md
new file mode 100644
index 0000000000..a96d1fcce5
--- /dev/null
+++ b/.changeset/many-mayflies-notice.md
@@ -0,0 +1,9 @@
+---
+'@backstage/plugin-azure-devops': patch
+'@backstage/plugin-azure-devops-backend': minor
+'@backstage/plugin-azure-devops-common': patch
+---
+
+refactor(`@backstage/plugin-azure-devops`): Consume types from `@backstage/plugin-azure-devops-common`.
+Stop re-exporting types from `@backstage/plugin-azure-devops-backend`.
+Added new types to `@backstage/plugin-azure-devops-common`.
diff --git a/.changeset/metal-impalas-allow.md b/.changeset/metal-impalas-allow.md
new file mode 100644
index 0000000000..8ad727f759
--- /dev/null
+++ b/.changeset/metal-impalas-allow.md
@@ -0,0 +1,15 @@
+---
+'@backstage/techdocs-common': patch
+---
+
+1. Techdocs publisher constructors now use parameter objects when being
+ instantiated
+
+2. Internal refactor of `LocalPublish` publisher to use `fromConfig` for
+ creation to be aligned with other publishers; this does not impact
+ `LocalPublish` usage.
+
+```diff
+- const publisher = new LocalPublish(config, logger, discovery);
++ const publisher = LocalPublish.fromConfig(config, logger, discovery);
+```
diff --git a/.changeset/modern-beers-tickle.md b/.changeset/modern-beers-tickle.md
new file mode 100644
index 0000000000..6ad9cc872d
--- /dev/null
+++ b/.changeset/modern-beers-tickle.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-auth-backend': patch
+---
+
+Only use settings that have a value when creating a new FirestoreKeyStore instance
diff --git a/.changeset/selfish-bags-punch.md b/.changeset/selfish-bags-punch.md
new file mode 100644
index 0000000000..1950da9357
--- /dev/null
+++ b/.changeset/selfish-bags-punch.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-auth-backend': patch
+---
+
+Added forwarding of the `audience` option for the SAML provider, making it possible to enable `audience` verification.
diff --git a/.changeset/seven-zoos-sparkle.md b/.changeset/seven-zoos-sparkle.md
new file mode 100644
index 0000000000..1399c3254e
--- /dev/null
+++ b/.changeset/seven-zoos-sparkle.md
@@ -0,0 +1,14 @@
+---
+'@backstage/create-app': patch
+---
+
+Removed the version pinning of the packages `graphql-language-service-interface` and `graphql-language-service-parser`. This should no longer be necessary.
+
+You can apply the same change in your repository by ensuring that the following does _NOT_ appear in your root `package.json`.
+
+```json
+"resolutions": {
+ "graphql-language-service-interface": "2.8.2",
+ "graphql-language-service-parser": "1.9.0"
+ },
+```
diff --git a/.changeset/tall-lions-return.md b/.changeset/tall-lions-return.md
new file mode 100644
index 0000000000..56bb0fc3dd
--- /dev/null
+++ b/.changeset/tall-lions-return.md
@@ -0,0 +1,5 @@
+---
+'@backstage/cli': patch
+---
+
+Bumped `@spotify/eslint-config-typescript` from `v10` to `v12`, dropping support for Node.js v12.
diff --git a/ADOPTERS.md b/ADOPTERS.md
index f0b2fa16f6..6736538fa0 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -64,3 +64,4 @@
| [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. |
| [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. |
| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling |
+| [Signal Iduna Group](https://www.signal-iduna.de/) | [Jonas Thomsen](https://github.com/JoThomsen) | Developer Portal, documentation, monitoring, service catalog for our insurance ecosystem
diff --git a/package.json b/package.json
index 62a8a0924e..03dd289653 100644
--- a/package.json
+++ b/package.json
@@ -46,9 +46,7 @@
"resolutions": {
"**/@graphql-codegen/cli/**/ws": "^7.4.6",
"**/@roadiehq/**/@backstage/plugin-catalog": "*",
- "**/@roadiehq/**/@backstage/catalog-model": "*",
- "graphql-language-service-interface": "2.8.2",
- "graphql-language-service-parser": "1.9.0"
+ "**/@roadiehq/**/@backstage/catalog-model": "*"
},
"version": "1.0.0",
"dependencies": {
diff --git a/packages/cli/package.json b/packages/cli/package.json
index c023313fe7..23a2bef027 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -43,7 +43,7 @@
"@rollup/plugin-yaml": "^3.0.0",
"@spotify/eslint-config-base": "^12.0.0",
"@spotify/eslint-config-react": "^10.0.0",
- "@spotify/eslint-config-typescript": "^10.0.0",
+ "@spotify/eslint-config-typescript": "^12.0.0",
"@sucrase/jest-plugin": "^2.1.1",
"@sucrase/webpack-loader": "^2.0.0",
"@svgr/plugin-jsx": "5.5.x",
diff --git a/packages/core-components/src/components/Table/Table.stories.tsx b/packages/core-components/src/components/Table/Table.stories.tsx
index 7785764b22..b057d0a12c 100644
--- a/packages/core-components/src/components/Table/Table.stories.tsx
+++ b/packages/core-components/src/components/Table/Table.stories.tsx
@@ -326,3 +326,49 @@ export const FilterTable = () => {
);
};
+
+export const StyledTable = () => {
+ const classes = useStyles();
+ const columns: TableColumn[] = [
+ {
+ title: 'Column 1',
+ field: 'col1',
+ highlight: true,
+ cellStyle: (_, rowData: any & { tableData: { id: number } }) => {
+ return rowData.tableData.id % 2 === 0
+ ? {
+ color: '#6CD75F',
+ }
+ : {
+ color: '#DC3D5A',
+ };
+ },
+ },
+ {
+ title: 'Column 2',
+ field: 'col2',
+ cellStyle: { color: '#2FA5DC' },
+ },
+ {
+ title: 'Numeric value',
+ field: 'number',
+ type: 'numeric',
+ },
+ {
+ title: 'A Date',
+ field: 'date',
+ type: 'date',
+ },
+ ];
+
+ return (
+
+ );
+};
diff --git a/packages/core-components/src/components/Table/Table.test.tsx b/packages/core-components/src/components/Table/Table.test.tsx
index fb231fde31..12431dea59 100644
--- a/packages/core-components/src/components/Table/Table.test.tsx
+++ b/packages/core-components/src/components/Table/Table.test.tsx
@@ -18,17 +18,18 @@ import React from 'react';
import { renderInTestApp } from '@backstage/test-utils';
import { Table } from './Table';
+const column1 = {
+ title: 'Column 1',
+ field: 'col1',
+};
+
+const column2 = {
+ title: 'Column 2',
+ field: 'col2',
+};
+
const minProps = {
- columns: [
- {
- title: 'Column 1',
- field: 'col1',
- },
- {
- title: 'Column 2',
- field: 'col2',
- },
- ],
+ columns: [column1, column2],
data: [
{
col1: 'first value, first row',
@@ -47,6 +48,100 @@ describe('', () => {
expect(rendered.getByText('second value, second row')).toBeInTheDocument();
});
+ describe('with style rows', () => {
+ describe('with CSS Properties object', () => {
+ const styledColumn2 = {
+ ...column2,
+ cellStyle: {
+ color: 'blue',
+ },
+ };
+
+ it('renders non-highlighted correctly', async () => {
+ const columns = [column1, styledColumn2];
+
+ const rendered = await renderInTestApp(
+ ,
+ );
+ expect(rendered.getByText('second value, first row')).toHaveStyle({
+ color: 'blue',
+ });
+ });
+
+ it('renders highlighted column correctly', async () => {
+ const columns = [
+ column1,
+ {
+ ...styledColumn2,
+ highlight: true,
+ },
+ ];
+
+ const rendered = await renderInTestApp(
+ ,
+ );
+ expect(rendered.getByText('second value, first row')).toHaveStyle({
+ color: 'blue',
+ 'font-weight': 700,
+ });
+ });
+ });
+
+ describe('with CSS Properties function', () => {
+ const styledColumn2 = {
+ ...column2,
+ cellStyle: (
+ _data: any,
+ rowData: any & { tableData: { id: number } },
+ ) => {
+ return rowData.tableData.id % 2 === 0
+ ? {
+ color: 'green',
+ }
+ : {
+ color: 'red',
+ };
+ },
+ };
+
+ it('renders non-highlighted columns correctly', async () => {
+ const columns = [column1, styledColumn2];
+
+ const rendered = await renderInTestApp(
+ ,
+ );
+ expect(rendered.getByText('second value, first row')).toHaveStyle({
+ color: 'green',
+ });
+ expect(rendered.getByText('second value, second row')).toHaveStyle({
+ color: 'red',
+ });
+ });
+
+ it('renders highlighted columns correctly', async () => {
+ const columns = [
+ column1,
+ {
+ ...styledColumn2,
+ highlight: true,
+ },
+ ];
+
+ const rendered = await renderInTestApp(
+ ,
+ );
+ expect(rendered.getByText('second value, first row')).toHaveStyle({
+ color: 'green',
+ 'font-weight': 700,
+ });
+ expect(rendered.getByText('second value, second row')).toHaveStyle({
+ color: 'red',
+ 'font-weight': 700,
+ });
+ });
+ });
+ });
+
it('renders with subtitle', async () => {
const rendered = await renderInTestApp(
,
diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx
index f433e7cdc5..e90ed04871 100644
--- a/packages/core-components/src/components/Table/Table.tsx
+++ b/packages/core-components/src/components/Table/Table.tsx
@@ -169,12 +169,26 @@ function convertColumns(
): TableColumn[] {
return columns.map(column => {
const headerStyle: React.CSSProperties = {};
- const cellStyle: React.CSSProperties =
- typeof column.cellStyle === 'object' ? column.cellStyle : {};
+
+ let cellStyle = column.cellStyle || {};
if (column.highlight) {
headerStyle.color = theme.palette.textContrast;
- cellStyle.fontWeight = theme.typography.fontWeightBold;
+
+ if (typeof cellStyle === 'object') {
+ (cellStyle as React.CSSProperties).fontWeight =
+ theme.typography.fontWeightBold;
+ } else {
+ const cellStyleFn = cellStyle as (
+ data: any,
+ rowData: T,
+ column?: Column,
+ ) => React.CSSProperties;
+ cellStyle = (data, rowData, rowColumn) => {
+ const style = cellStyleFn(data, rowData, rowColumn);
+ return { ...style, fontWeight: theme.typography.fontWeightBold };
+ };
+ }
}
return {
diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs
index 1db9a98c41..bdae71d9e3 100644
--- a/packages/create-app/templates/default-app/package.json.hbs
+++ b/packages/create-app/templates/default-app/package.json.hbs
@@ -23,10 +23,6 @@
"create-plugin": "backstage-cli create-plugin --scope internal",
"remove-plugin": "backstage-cli remove-plugin"
},
- "resolutions": {
- "graphql-language-service-interface": "2.8.2",
- "graphql-language-service-parser": "1.9.0"
- },
"workspaces": {
"packages": [
"packages/*",
diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts
index f0616e4cd3..5ebc2aab77 100644
--- a/packages/techdocs-common/src/stages/publish/awsS3.ts
+++ b/packages/techdocs-common/src/stages/publish/awsS3.ts
@@ -57,6 +57,26 @@ const streamToBuffer = (stream: Readable): Promise => {
};
export class AwsS3Publish implements PublisherBase {
+ private readonly storageClient: aws.S3;
+ private readonly bucketName: string;
+ private readonly legacyPathCasing: boolean;
+ private readonly logger: Logger;
+ private readonly bucketRootPath: string;
+
+ constructor(options: {
+ storageClient: aws.S3;
+ bucketName: string;
+ legacyPathCasing: boolean;
+ logger: Logger;
+ bucketRootPath: string;
+ }) {
+ this.storageClient = options.storageClient;
+ this.bucketName = options.bucketName;
+ this.legacyPathCasing = options.legacyPathCasing;
+ this.logger = options.logger;
+ this.bucketRootPath = options.bucketRootPath;
+ }
+
static fromConfig(config: Config, logger: Logger): PublisherBase {
let bucketName = '';
try {
@@ -112,13 +132,13 @@ export class AwsS3Publish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
- return new AwsS3Publish(
+ return new AwsS3Publish({
storageClient,
bucketName,
+ bucketRootPath,
legacyPathCasing,
logger,
- bucketRootPath,
- );
+ });
}
private static buildCredentials(
@@ -152,20 +172,6 @@ export class AwsS3Publish implements PublisherBase {
return explicitCredentials;
}
- constructor(
- private readonly storageClient: aws.S3,
- private readonly bucketName: string,
- private readonly legacyPathCasing: boolean,
- private readonly logger: Logger,
- private readonly bucketRootPath: string,
- ) {
- this.storageClient = storageClient;
- this.bucketName = bucketName;
- this.legacyPathCasing = legacyPathCasing;
- this.logger = logger;
- this.bucketRootPath = bucketRootPath;
- }
-
/**
* Check if the defined bucket exists. Being able to connect means the configuration is good
* and the storage client will work.
diff --git a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts
index f1d60f709a..7a58d92095 100644
--- a/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts
+++ b/packages/techdocs-common/src/stages/publish/azureBlobStorage.ts
@@ -47,6 +47,23 @@ import {
const BATCH_CONCURRENCY = 3;
export class AzureBlobStoragePublish implements PublisherBase {
+ private readonly storageClient: BlobServiceClient;
+ private readonly containerName: string;
+ private readonly legacyPathCasing: boolean;
+ private readonly logger: Logger;
+
+ constructor(options: {
+ storageClient: BlobServiceClient;
+ containerName: string;
+ legacyPathCasing: boolean;
+ logger: Logger;
+ }) {
+ this.storageClient = options.storageClient;
+ this.containerName = options.containerName;
+ this.legacyPathCasing = options.legacyPathCasing;
+ this.logger = options.logger;
+ }
+
static fromConfig(config: Config, logger: Logger): PublisherBase {
let containerName = '';
try {
@@ -95,24 +112,12 @@ export class AzureBlobStoragePublish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
- return new AzureBlobStoragePublish(
- storageClient,
- containerName,
- legacyPathCasing,
- logger,
- );
- }
-
- constructor(
- private readonly storageClient: BlobServiceClient,
- private readonly containerName: string,
- private readonly legacyPathCasing: boolean,
- private readonly logger: Logger,
- ) {
- this.storageClient = storageClient;
- this.containerName = containerName;
- this.legacyPathCasing = legacyPathCasing;
- this.logger = logger;
+ return new AzureBlobStoragePublish({
+ storageClient: storageClient,
+ containerName: containerName,
+ legacyPathCasing: legacyPathCasing,
+ logger: logger,
+ });
}
async getReadiness(): Promise {
diff --git a/packages/techdocs-common/src/stages/publish/googleStorage.ts b/packages/techdocs-common/src/stages/publish/googleStorage.ts
index 77c366315c..f072412c20 100644
--- a/packages/techdocs-common/src/stages/publish/googleStorage.ts
+++ b/packages/techdocs-common/src/stages/publish/googleStorage.ts
@@ -41,6 +41,26 @@ import {
} from './types';
export class GoogleGCSPublish implements PublisherBase {
+ private readonly storageClient: Storage;
+ private readonly bucketName: string;
+ private readonly legacyPathCasing: boolean;
+ private readonly logger: Logger;
+ private readonly bucketRootPath: string;
+
+ constructor(options: {
+ storageClient: Storage;
+ bucketName: string;
+ legacyPathCasing: boolean;
+ logger: Logger;
+ bucketRootPath: string;
+ }) {
+ this.storageClient = options.storageClient;
+ this.bucketName = options.bucketName;
+ this.legacyPathCasing = options.legacyPathCasing;
+ this.logger = options.logger;
+ this.bucketRootPath = options.bucketRootPath;
+ }
+
static fromConfig(config: Config, logger: Logger): PublisherBase {
let bucketName = '';
try {
@@ -84,27 +104,13 @@ export class GoogleGCSPublish implements PublisherBase {
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
- return new GoogleGCSPublish(
+ return new GoogleGCSPublish({
storageClient,
bucketName,
legacyPathCasing,
logger,
bucketRootPath,
- );
- }
-
- constructor(
- private readonly storageClient: Storage,
- private readonly bucketName: string,
- private readonly legacyPathCasing: boolean,
- private readonly logger: Logger,
- private readonly bucketRootPath: string,
- ) {
- this.storageClient = storageClient;
- this.bucketName = bucketName;
- this.legacyPathCasing = legacyPathCasing;
- this.logger = logger;
- this.bucketRootPath = bucketRootPath;
+ });
}
/**
diff --git a/packages/techdocs-common/src/stages/publish/local.test.ts b/packages/techdocs-common/src/stages/publish/local.test.ts
index 38ead236bd..de56580e02 100644
--- a/packages/techdocs-common/src/stages/publish/local.test.ts
+++ b/packages/techdocs-common/src/stages/publish/local.test.ts
@@ -63,7 +63,11 @@ describe('local publisher', () => {
const mockConfig = new ConfigReader({});
- const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
+ const publisher = LocalPublish.fromConfig(
+ mockConfig,
+ logger,
+ testDiscovery,
+ );
const mockEntity = createMockEntity();
const lowerMockEntity = createMockEntity(undefined, true);
@@ -90,7 +94,11 @@ describe('local publisher', () => {
},
});
- const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
+ const publisher = LocalPublish.fromConfig(
+ mockConfig,
+ logger,
+ testDiscovery,
+ );
const mockEntity = createMockEntity();
const lowerMockEntity = createMockEntity(undefined, true);
@@ -106,7 +114,11 @@ describe('local publisher', () => {
describe('docsRouter', () => {
const mockConfig = new ConfigReader({});
- const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
+ const publisher = LocalPublish.fromConfig(
+ mockConfig,
+ logger,
+ testDiscovery,
+ );
let app: express.Express;
beforeEach(() => {
@@ -166,7 +178,7 @@ describe('local publisher', () => {
legacyUseCaseSensitiveTripletPaths: true,
},
});
- const legacyPublisher = new LocalPublish(
+ const legacyPublisher = LocalPublish.fromConfig(
legacyConfig,
logger,
testDiscovery,
diff --git a/packages/techdocs-common/src/stages/publish/local.ts b/packages/techdocs-common/src/stages/publish/local.ts
index de0072de7a..70b4eb3ff2 100644
--- a/packages/techdocs-common/src/stages/publish/local.ts
+++ b/packages/techdocs-common/src/stages/publish/local.ts
@@ -59,24 +59,37 @@ try {
* called "static" at the root of techdocs-backend plugin.
*/
export class LocalPublish implements PublisherBase {
- private legacyPathCasing: boolean;
+ private readonly legacyPathCasing: boolean;
+ private readonly logger: Logger;
+ private readonly discovery: PluginEndpointDiscovery;
- // TODO: Use a static fromConfig method to create a LocalPublish instance, similar to aws/gcs publishers.
- // Move the logic of setting staticDocsDir based on config over to fromConfig,
- // and set the value as a class parameter.
- constructor(
- // @ts-ignore
- private readonly config: Config,
- private readonly logger: Logger,
- private readonly discovery: PluginEndpointDiscovery,
- ) {
- this.config = config;
- this.logger = logger;
- this.discovery = discovery;
- this.legacyPathCasing =
+ // TODO: Move the logic of setting staticDocsDir based on config over to
+ // fromConfig, and set the value as a class parameter.
+ constructor(options: {
+ logger: Logger;
+ discovery: PluginEndpointDiscovery;
+ legacyPathCasing: boolean;
+ }) {
+ this.logger = options.logger;
+ this.discovery = options.discovery;
+ this.legacyPathCasing = options.legacyPathCasing;
+ }
+
+ static fromConfig(
+ config: Config,
+ logger: Logger,
+ discovery: PluginEndpointDiscovery,
+ ): PublisherBase {
+ const legacyPathCasing =
config.getOptionalBoolean(
'techdocs.legacyUseCaseSensitiveTripletPaths',
) || false;
+
+ return new LocalPublish({
+ logger,
+ discovery,
+ legacyPathCasing,
+ });
}
async getReadiness(): Promise {
diff --git a/packages/techdocs-common/src/stages/publish/openStackSwift.ts b/packages/techdocs-common/src/stages/publish/openStackSwift.ts
index 51e7b76406..a463157552 100644
--- a/packages/techdocs-common/src/stages/publish/openStackSwift.ts
+++ b/packages/techdocs-common/src/stages/publish/openStackSwift.ts
@@ -54,6 +54,20 @@ const bufferToStream = (buffer: Buffer): Readable => {
};
export class OpenStackSwiftPublish implements PublisherBase {
+ private readonly storageClient: SwiftClient;
+ private readonly containerName: string;
+ private readonly logger: Logger;
+
+ constructor(options: {
+ storageClient: SwiftClient;
+ containerName: string;
+ logger: Logger;
+ }) {
+ this.storageClient = options.storageClient;
+ this.containerName = options.containerName;
+ this.logger = options.logger;
+ }
+
static fromConfig(config: Config, logger: Logger): PublisherBase {
let containerName = '';
try {
@@ -78,17 +92,7 @@ export class OpenStackSwiftPublish implements PublisherBase {
secret: openStackSwiftConfig.getString('credentials.secret'),
});
- return new OpenStackSwiftPublish(storageClient, containerName, logger);
- }
-
- constructor(
- private readonly storageClient: SwiftClient,
- private readonly containerName: string,
- private readonly logger: Logger,
- ) {
- this.storageClient = storageClient;
- this.containerName = containerName;
- this.logger = logger;
+ return new OpenStackSwiftPublish({ storageClient, containerName, logger });
}
/*
diff --git a/packages/techdocs-common/src/stages/publish/publish.ts b/packages/techdocs-common/src/stages/publish/publish.ts
index c7c6154114..eb9e3104bf 100644
--- a/packages/techdocs-common/src/stages/publish/publish.ts
+++ b/packages/techdocs-common/src/stages/publish/publish.ts
@@ -61,10 +61,10 @@ export class Publisher {
return OpenStackSwiftPublish.fromConfig(config, logger);
case 'local':
logger.info('Creating Local publisher for TechDocs');
- return new LocalPublish(config, logger, discovery);
+ return LocalPublish.fromConfig(config, logger, discovery);
default:
logger.info('Creating Local publisher for TechDocs');
- return new LocalPublish(config, logger, discovery);
+ return LocalPublish.fromConfig(config, logger, discovery);
}
}
}
diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts
index 25da7fa332..791bd3606b 100644
--- a/plugins/auth-backend/config.d.ts
+++ b/plugins/auth-backend/config.d.ts
@@ -75,6 +75,7 @@ export interface Config {
logoutUrl?: string;
issuer: string;
cert: string;
+ audience?: string;
privateKey?: string;
authnContext?: string[];
identifierFormat?: string;
diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts
index 74e60a0502..3d238adba9 100644
--- a/plugins/auth-backend/src/identity/KeyStores.ts
+++ b/plugins/auth-backend/src/identity/KeyStores.ts
@@ -15,6 +15,7 @@
*/
import { Logger } from 'winston';
+import { pickBy } from 'lodash';
import { PluginDatabaseManager } from '@backstage/backend-common';
import { Config } from '@backstage/config';
@@ -64,16 +65,20 @@ export class KeyStores {
if (provider === 'firestore') {
const settings = ks?.getConfig(provider);
- const keyStore = await FirestoreKeyStore.create({
- projectId: settings?.getOptionalString('projectId'),
- keyFilename: settings?.getOptionalString('keyFilename'),
- host: settings?.getOptionalString('host'),
- port: settings?.getOptionalNumber('port'),
- ssl: settings?.getOptionalBoolean('ssl'),
- path: settings?.getOptionalString('path'),
- timeout: settings?.getOptionalNumber('timeout'),
- });
-
+ const keyStore = await FirestoreKeyStore.create(
+ pickBy(
+ {
+ projectId: settings?.getOptionalString('projectId'),
+ keyFilename: settings?.getOptionalString('keyFilename'),
+ host: settings?.getOptionalString('host'),
+ port: settings?.getOptionalNumber('port'),
+ ssl: settings?.getOptionalBoolean('ssl'),
+ path: settings?.getOptionalString('path'),
+ timeout: settings?.getOptionalNumber('timeout'),
+ },
+ value => value !== undefined,
+ ),
+ );
await FirestoreKeyStore.verifyConnection(keyStore, logger);
return keyStore;
diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts
index 07fc77e4f7..8929a1dbf0 100644
--- a/plugins/auth-backend/src/providers/saml/provider.ts
+++ b/plugins/auth-backend/src/providers/saml/provider.ts
@@ -125,6 +125,7 @@ export const createSamlProvider = (
callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
entryPoint: config.getString('entryPoint'),
logoutUrl: config.getOptionalString('logoutUrl'),
+ audience: config.getOptionalString('audience'),
issuer: config.getString('issuer'),
cert: config.getString('cert'),
privateCert: config.getOptionalString('privateKey'),
diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md
index fac6261a39..1eabccc720 100644
--- a/plugins/azure-devops-backend/api-report.md
+++ b/plugins/azure-devops-backend/api-report.md
@@ -4,13 +4,13 @@
```ts
import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces';
-import { BuildResult } from 'azure-devops-node-api/interfaces/BuildInterfaces';
-import { BuildStatus } from 'azure-devops-node-api/interfaces/BuildInterfaces';
import { Config } from '@backstage/config';
import express from 'express';
import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces';
import { Logger as Logger_2 } from 'winston';
-import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces';
+import { PullRequest } from '@backstage/plugin-azure-devops-common';
+import { PullRequestOptions } from '@backstage/plugin-azure-devops-common';
+import { RepoBuild } from '@backstage/plugin-azure-devops-common';
import { WebApi } from 'azure-devops-node-api';
// Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -29,8 +29,6 @@ export class AzureDevOpsApi {
projectName: string,
repoName: string,
): Promise;
- // Warning: (ae-forgotten-export) The symbol "PullRequestOptions" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
getPullRequests(
projectName: string,
@@ -45,48 +43,11 @@ export class AzureDevOpsApi {
): Promise;
}
-export { BuildResult };
-
-export { BuildStatus };
-
// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function createRouter(options: RouterOptions): Promise;
-// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export type PullRequest = {
- pullRequestId?: number;
- repoName?: string;
- title?: string;
- uniqueName?: string;
- createdBy?: string;
- creationDate?: Date;
- sourceRefName?: string;
- targetRefName?: string;
- status?: PullRequestStatus;
- isDraft?: boolean;
- link: string;
-};
-
-// Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export type RepoBuild = {
- id?: number;
- title: string;
- link?: string;
- status?: BuildStatus;
- result?: BuildResult;
- queueTime?: Date;
- startTime?: Date;
- finishTime?: Date;
- source: string;
- uniqueName?: string;
-};
-
// Warning: (ae-missing-release-tag) "RouterOptions" 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-backend/package.json b/plugins/azure-devops-backend/package.json
index 8a468b0cc9..9dd79e9f8e 100644
--- a/plugins/azure-devops-backend/package.json
+++ b/plugins/azure-devops-backend/package.json
@@ -22,6 +22,7 @@
"dependencies": {
"@backstage/backend-common": "^0.9.8",
"@backstage/config": "^0.1.11",
+ "@backstage/plugin-azure-devops-common": "^0.0.1",
"@types/express": "^4.17.6",
"azure-devops-node-api": "^11.0.1",
"express": "^4.17.1",
diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
index fdd49c0440..429d608bd6 100644
--- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
+++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts
@@ -16,17 +16,21 @@
import {
Build,
+ DefinitionReference,
+} from 'azure-devops-node-api/interfaces/BuildInterfaces';
+import {
BuildResult,
BuildStatus,
- GitPullRequest,
- GitRepository,
PullRequest,
PullRequestStatus,
RepoBuild,
-} from './types';
+} from '@backstage/plugin-azure-devops-common';
+import {
+ GitPullRequest,
+ GitRepository,
+} from 'azure-devops-node-api/interfaces/GitInterfaces';
import { mappedPullRequest, mappedRepoBuild } from './AzureDevOpsApi';
-import { DefinitionReference } from 'azure-devops-node-api/interfaces/BuildInterfaces';
import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces';
describe('AzureDevOpsApi', () => {
diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
index 26378496ea..1ed1f1d5f5 100644
--- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
+++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts
@@ -15,17 +15,19 @@
*/
import {
- Build,
BuildResult,
BuildStatus,
- GitPullRequest,
- GitPullRequestSearchCriteria,
- GitRepository,
PullRequest,
PullRequestOptions,
RepoBuild,
-} from './types';
+} from '@backstage/plugin-azure-devops-common';
+import {
+ GitPullRequest,
+ GitPullRequestSearchCriteria,
+ GitRepository,
+} from 'azure-devops-node-api/interfaces/GitInterfaces';
+import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces';
import { Logger } from 'winston';
import { WebApi } from 'azure-devops-node-api';
diff --git a/plugins/azure-devops-backend/src/api/index.ts b/plugins/azure-devops-backend/src/api/index.ts
index 97c48cb0bf..903c1bdb6a 100644
--- a/plugins/azure-devops-backend/src/api/index.ts
+++ b/plugins/azure-devops-backend/src/api/index.ts
@@ -15,5 +15,3 @@
*/
export { AzureDevOpsApi } from './AzureDevOpsApi';
-export { BuildResult, BuildStatus } from './types';
-export type { RepoBuild, PullRequest } from './types';
diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts
deleted file mode 100644
index fab61d26c9..0000000000
--- a/plugins/azure-devops-backend/src/api/types.ts
+++ /dev/null
@@ -1,67 +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.
- */
-
-import {
- Build,
- BuildResult,
- BuildStatus,
-} from 'azure-devops-node-api/interfaces/BuildInterfaces';
-import {
- GitPullRequest,
- GitPullRequestSearchCriteria,
- GitRepository,
- PullRequestStatus,
-} from 'azure-devops-node-api/interfaces/GitInterfaces';
-
-export { BuildResult, BuildStatus, PullRequestStatus };
-export type {
- Build,
- GitPullRequest,
- GitPullRequestSearchCriteria,
- GitRepository,
-};
-
-export type RepoBuild = {
- id?: number;
- title: string;
- link?: string;
- status?: BuildStatus;
- result?: BuildResult;
- queueTime?: Date;
- startTime?: Date;
- finishTime?: Date;
- source: string;
- uniqueName?: string;
-};
-
-export type PullRequest = {
- pullRequestId?: number;
- repoName?: string;
- title?: string;
- uniqueName?: string;
- createdBy?: string;
- creationDate?: Date;
- sourceRefName?: string;
- targetRefName?: string;
- status?: PullRequestStatus;
- isDraft?: boolean;
- link: string;
-};
-
-export type PullRequestOptions = {
- top: number;
- status: PullRequestStatus;
-};
diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts
index 7a2a347b65..6977d3580f 100644
--- a/plugins/azure-devops-backend/src/index.ts
+++ b/plugins/azure-devops-backend/src/index.ts
@@ -13,6 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-export { AzureDevOpsApi, BuildResult, BuildStatus } from './api';
-export type { RepoBuild, PullRequest } from './api';
+export { AzureDevOpsApi } from './api';
export * from './service/router';
diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts
index 4143291409..f5d533d2d8 100644
--- a/plugins/azure-devops-backend/src/service/router.test.ts
+++ b/plugins/azure-devops-backend/src/service/router.test.ts
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-import { getVoidLogger } from '@backstage/backend-common';
-import { ConfigReader } from '@backstage/config';
-import express from 'express';
-import request from 'supertest';
-import { AzureDevOpsApi } from '../api';
-import { createRouter } from './router';
-import { PullRequest, RepoBuild } from '../api/types';
import {
- GitRepository,
- PullRequestStatus,
-} from 'azure-devops-node-api/interfaces/GitInterfaces';
-import {
- Build,
BuildResult,
BuildStatus,
-} from 'azure-devops-node-api/interfaces/BuildInterfaces';
+ PullRequest,
+ PullRequestStatus,
+ RepoBuild,
+} from '@backstage/plugin-azure-devops-common';
+
+import { AzureDevOpsApi } from '../api';
+import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces';
+import { ConfigReader } from '@backstage/config';
+import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces';
+import { createRouter } from './router';
+import express from 'express';
+import { getVoidLogger } from '@backstage/backend-common';
+import request from 'supertest';
describe('createRouter', () => {
let azureDevOpsApi: jest.Mocked;
diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts
index b3505eae1c..d61148b4ff 100644
--- a/plugins/azure-devops-backend/src/service/router.ts
+++ b/plugins/azure-devops-backend/src/service/router.ts
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-import { PullRequestOptions, PullRequestStatus } from '../api/types';
+import {
+ PullRequestOptions,
+ PullRequestStatus,
+} from '@backstage/plugin-azure-devops-common';
import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api';
import { AzureDevOpsApi } from '../api';
diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md
index 0bc6568189..f03c0b4059 100644
--- a/plugins/azure-devops-common/api-report.md
+++ b/plugins/azure-devops-common/api-report.md
@@ -27,6 +27,42 @@ export enum BuildStatus {
Postponed = 8,
}
+// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type PullRequest = {
+ pullRequestId?: number;
+ repoName?: string;
+ title?: string;
+ uniqueName?: string;
+ createdBy?: string;
+ creationDate?: Date;
+ sourceRefName?: string;
+ targetRefName?: string;
+ status?: PullRequestStatus;
+ isDraft?: boolean;
+ link: string;
+};
+
+// Warning: (ae-missing-release-tag) "PullRequestOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type PullRequestOptions = {
+ top: number;
+ status: PullRequestStatus;
+};
+
+// Warning: (ae-missing-release-tag) "PullRequestStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export enum PullRequestStatus {
+ Abandoned = 2,
+ Active = 1,
+ All = 4,
+ Completed = 3,
+ NotSet = 0,
+}
+
// Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -43,5 +79,12 @@ export type RepoBuild = {
uniqueName?: string;
};
+// Warning: (ae-missing-release-tag) "RepoBuildOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type RepoBuildOptions = {
+ top?: number;
+};
+
// (No @packageDocumentation comment for this package)
```
diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts
index d7736d03e1..eacc2af30d 100644
--- a/plugins/azure-devops-common/src/types.ts
+++ b/plugins/azure-devops-common/src/types.ts
@@ -80,3 +80,49 @@ export type RepoBuild = {
source: string;
uniqueName?: string;
};
+
+export type RepoBuildOptions = {
+ top?: number;
+};
+
+export enum PullRequestStatus {
+ /**
+ * Status not set. Default state.
+ */
+ NotSet = 0,
+ /**
+ * Pull request is active.
+ */
+ Active = 1,
+ /**
+ * Pull request is abandoned.
+ */
+ Abandoned = 2,
+ /**
+ * Pull request is completed.
+ */
+ Completed = 3,
+ /**
+ * Used in pull request search criteria to include all statuses.
+ */
+ All = 4,
+}
+
+export type PullRequest = {
+ pullRequestId?: number;
+ repoName?: string;
+ title?: string;
+ uniqueName?: string;
+ createdBy?: string;
+ creationDate?: Date;
+ sourceRefName?: string;
+ targetRefName?: string;
+ status?: PullRequestStatus;
+ isDraft?: boolean;
+ link: string;
+};
+
+export type PullRequestOptions = {
+ top: number;
+ status: PullRequestStatus;
+};
diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json
index 1ad5b07ebb..25328d9e81 100644
--- a/plugins/azure-devops/package.json
+++ b/plugins/azure-devops/package.json
@@ -37,7 +37,6 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
- "azure-devops-node-api": "^11.0.1",
"luxon": "^2.0.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
diff --git a/plugins/azure-devops/src/api/AzureDevOpsApi.ts b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
index f0bad53b43..05377b0b7b 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsApi.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsApi.ts
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-import { RepoBuild, RepoBuildOptions } from './types';
+import {
+ RepoBuild,
+ RepoBuildOptions,
+} from '@backstage/plugin-azure-devops-common';
+
import { createApiRef } from '@backstage/core-plugin-api';
export const azureDevOpsApiRef = createApiRef({
diff --git a/plugins/azure-devops/src/api/AzureDevOpsClient.ts b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
index e03428e511..4fd4e781c4 100644
--- a/plugins/azure-devops/src/api/AzureDevOpsClient.ts
+++ b/plugins/azure-devops/src/api/AzureDevOpsClient.ts
@@ -14,9 +14,13 @@
* limitations under the License.
*/
-import { AzureDevOpsApi } from './AzureDevOpsApi';
-import { RepoBuild, RepoBuildOptions } from './types';
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
+import {
+ RepoBuild,
+ RepoBuildOptions,
+} from '@backstage/plugin-azure-devops-common';
+
+import { AzureDevOpsApi } from './AzureDevOpsApi';
import { ResponseError } from '@backstage/errors';
export class AzureDevOpsClient implements AzureDevOpsApi {
diff --git a/plugins/azure-devops/src/api/types.ts b/plugins/azure-devops/src/api/types.ts
deleted file mode 100644
index 1fbc34231e..0000000000
--- a/plugins/azure-devops/src/api/types.ts
+++ /dev/null
@@ -1,34 +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.
- */
-
-import {
- BuildResult,
- BuildStatus,
-} from 'azure-devops-node-api/interfaces/BuildInterfaces';
-
-export type RepoBuild = {
- id?: number;
- title: string;
- link?: string;
- status?: BuildStatus;
- result?: BuildResult;
- queueTime?: Date;
- source: string;
-};
-
-export type RepoBuildOptions = {
- top?: number;
-};
diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts
index 42408f37b3..cb240aef42 100644
--- a/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts
+++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.test.ts
@@ -17,7 +17,7 @@
import {
BuildResult,
BuildStatus,
-} from 'azure-devops-node-api/interfaces/BuildInterfaces';
+} from '@backstage/plugin-azure-devops-common';
import { getBuildResultComponent, getBuildStateComponent } from './BuildTable';
import { renderInTestApp } from '@backstage/test-utils';
diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
index f8bb031f0a..5667c65711 100644
--- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
+++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx
@@ -18,7 +18,8 @@ import { Box, Typography } from '@material-ui/core';
import {
BuildResult,
BuildStatus,
-} from 'azure-devops-node-api/interfaces/BuildInterfaces';
+ RepoBuild,
+} from '@backstage/plugin-azure-devops-common';
import {
Link,
ResponseErrorPanel,
@@ -34,7 +35,6 @@ import {
import { DateTime } from 'luxon';
import React from 'react';
-import { RepoBuild } from '../../api/types';
export const getBuildResultComponent = (result: number | undefined) => {
switch (result) {
diff --git a/plugins/azure-devops/src/hooks/useRepoBuilds.ts b/plugins/azure-devops/src/hooks/useRepoBuilds.ts
index df4f76c57e..af2d181c34 100644
--- a/plugins/azure-devops/src/hooks/useRepoBuilds.ts
+++ b/plugins/azure-devops/src/hooks/useRepoBuilds.ts
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-import { RepoBuild, RepoBuildOptions } from '../api/types';
+import {
+ RepoBuild,
+ RepoBuildOptions,
+} from '@backstage/plugin-azure-devops-common';
import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants';
import { Entity } from '@backstage/catalog-model';
diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts
index cb9c73d7b1..bf13859c41 100644
--- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts
+++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.test.ts
@@ -21,6 +21,14 @@ describe('normalizeEntityName', () => {
expect(normalizeEntityName('User Name')).toBe('user_name');
});
+ it('should normalize complex name to valid entity name', () => {
+ expect(normalizeEntityName('User (Name)')).toBe('user_name');
+ });
+
+ it('should normalize complex name to valid entity name without extra underscore', () => {
+ expect(normalizeEntityName('User :(Name:)')).toBe('user_name');
+ });
+
it('should normalize e-mail to valid entity name', () => {
expect(normalizeEntityName('user.name@example.com')).toBe(
'user.name_example.com',
diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts
index c1d2606018..41dbc1aed8 100644
--- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts
+++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/helper.ts
@@ -15,8 +15,21 @@
*/
export function normalizeEntityName(name: string): string {
- return name
+ let cleaned = name
.trim()
.toLocaleLowerCase()
.replace(/[^a-zA-Z0-9_\-\.]/g, '_');
+
+ // invalid to end with _
+ while (cleaned.endsWith('_')) {
+ cleaned = cleaned.substring(0, cleaned.length - 1);
+ }
+
+ // cleans up format for groups like 'my group (Reader)'
+ while (cleaned.includes('__')) {
+ // replaceAll from node.js >= 15
+ cleaned = cleaned.replace('__', '_');
+ }
+
+ return cleaned;
}
diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts
index fa86d77b27..b0273e1f58 100644
--- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts
+++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.test.ts
@@ -445,6 +445,92 @@ describe('read microsoft graph', () => {
// expect(client.getGroupPhotoWithSizeLimit).toBeCalledTimes(1);
// expect(client.getGroupPhotoWithSizeLimit).toBeCalledWith('groupid', 120);
});
+
+ it('should read security groups', async () => {
+ async function* getExampleGroups() {
+ yield {
+ id: 'groupid',
+ displayName: 'Group Name',
+ description: 'Group Description',
+ mail: 'group@example.com',
+ mailNickname: 'df546d53-4f5f-4462-b371-d4a855787047',
+ mailEnabled: false,
+ securityEnabled: true,
+ };
+ }
+
+ async function* getExampleGroupMembers(): AsyncIterable {
+ yield {
+ '@odata.type': '#microsoft.graph.group',
+ id: 'childgroupid',
+ };
+ yield {
+ '@odata.type': '#microsoft.graph.user',
+ id: 'userid',
+ };
+ }
+
+ client.getGroups.mockImplementation(getExampleGroups);
+ client.getGroupMembers.mockImplementation(getExampleGroupMembers);
+ client.getOrganization.mockResolvedValue({
+ id: 'tenantid',
+ displayName: 'Organization Name',
+ });
+ client.getGroupPhotoWithSizeLimit.mockResolvedValue(
+ 'data:image/jpeg;base64,...',
+ );
+
+ const { groups, rootGroup } = await readMicrosoftGraphGroups(
+ client,
+ 'tenantid',
+ {
+ groupFilter: 'securityEnabled eq true',
+ },
+ );
+
+ const expectedRootGroup = group({
+ metadata: {
+ annotations: {
+ 'graph.microsoft.com/tenant-id': 'tenantid',
+ },
+ name: 'organization_name',
+ description: 'Organization Name',
+ },
+ spec: {
+ type: 'root',
+ profile: {
+ displayName: 'Organization Name',
+ },
+ children: [],
+ },
+ });
+ expect(groups).toEqual([
+ expectedRootGroup,
+ group({
+ metadata: {
+ annotations: {
+ 'graph.microsoft.com/group-id': 'groupid',
+ },
+ name: 'group_name',
+ description: 'Group Description',
+ },
+ spec: {
+ type: 'team',
+ profile: {
+ displayName: 'Group Name',
+ email: 'group@example.com',
+ },
+ children: [],
+ },
+ }),
+ ]);
+ expect(rootGroup).toEqual(expectedRootGroup);
+ expect(client.getGroups).toBeCalledWith({
+ filter: 'securityEnabled eq true',
+ });
+ expect(client.getGroupMembers).toBeCalledTimes(1);
+ expect(client.getGroupMembers).toBeCalledWith('groupid');
+ });
});
describe('resolveRelations', () => {
diff --git a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts
index fa229ca956..7dd6f83e2e 100644
--- a/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts
+++ b/plugins/catalog-backend-module-msgraph/src/microsoftGraph/read.ts
@@ -251,6 +251,13 @@ export async function readMicrosoftGraphOrganization(
return { rootGroup };
}
+function extractGroupName(group: MicrosoftGraph.Group): string {
+ if (group.securityEnabled) {
+ return group.displayName as string;
+ }
+ return (group.mailNickname || group.displayName) as string;
+}
+
export async function defaultGroupTransformer(
group: MicrosoftGraph.Group,
groupPhoto?: string,
@@ -259,7 +266,7 @@ export async function defaultGroupTransformer(
return undefined;
}
- const name = normalizeEntityName(group.mailNickname || group.displayName);
+ const name = normalizeEntityName(extractGroupName(group));
const entity: GroupEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts
index 3f6a0394d3..4d44c021c4 100644
--- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts
+++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts
@@ -79,10 +79,14 @@ export class KubernetesBackendClient implements KubernetesApi {
}
async getClusters(): Promise<{ name: string; authProvider: string }[]> {
+ const idToken = await this.identityApi.getIdToken();
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/clusters`;
const response = await fetch(url, {
method: 'GET',
+ headers: {
+ ...(idToken && { Authorization: `Bearer ${idToken}` }),
+ },
});
return (await this.handleResponse(response)).items;
diff --git a/yarn.lock b/yarn.lock
index 98e8a242cb..e2e7c13672 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5486,10 +5486,10 @@
resolved "https://registry.npmjs.org/@spotify/eslint-config-react/-/eslint-config-react-10.0.0.tgz#6f83ada05f79b49c1f9def5b8815e3231ed24969"
integrity sha512-MozX6W3aMp7EQPliuUQYI58Ni5vh65mItXMG0CgZBj0v1ZEeZVM5XS/nqhsCaIHYXskmZM2O1qqLFaEg5PqGdg==
-"@spotify/eslint-config-typescript@^10.0.0":
- version "10.0.0"
- resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-10.0.0.tgz#4df7074f3f4ef31d76c617e55d335f9a36cfed5b"
- integrity sha512-qR4WOU3gJrpz26O8BlNbXas4Yj93NeVH7yvULVYO2j9bCAEZJu2sfl1BGfOy4qAsYGutZhJtNwMqK0Rl4DcFHQ==
+"@spotify/eslint-config-typescript@^12.0.0":
+ version "12.0.0"
+ resolved "https://registry.npmjs.org/@spotify/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz#4c7af3f74a47668bec0c860b72e2a0103e78a138"
+ integrity sha512-nMVll8ZkN/W8+IHn6Iz3YzCKW0qhrn3TVfyxkAr3qmXm5cex+GzyUdZEuxb8rdN2inZL6A1Il2NFfO5p/UKxog==
"@spotify/prettier-config@^11.0.0":
version "11.0.0"
@@ -15591,20 +15591,20 @@ graphql-extensions@^0.15.0:
apollo-server-env "^3.1.0"
apollo-server-types "^0.9.0"
-graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.9.0:
- version "2.8.2"
- resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.8.2.tgz#b3bb2aef7eaf0dff0b4ea419fa412c5f66fa268b"
- integrity sha512-otbOQmhgkAJU1QJgQkMztNku6SbJLu/uodoFOYOOtJsizTjrMs93vkYaHCcYnLA3oi1Goj27XcHjMnRCYQOZXQ==
+graphql-language-service-interface@^2.9.0:
+ version "2.9.1"
+ resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.9.1.tgz#be0b11b06b78730ea9d250e0e2290e7ed9c8d283"
+ integrity sha512-yGsE67fxJBXxY82+rLDMvUpmzpOUM8XFB+k+xOTUyABWs27osKaoGiuDDXAVGg1adhm+cpunWbipe763ZJkAVA==
dependencies:
- graphql-language-service-parser "^1.9.0"
- graphql-language-service-types "^1.8.0"
- graphql-language-service-utils "^2.5.1"
+ graphql-language-service-parser "^1.10.0"
+ graphql-language-service-types "^1.8.3"
+ graphql-language-service-utils "^2.6.0"
vscode-languageserver-types "^3.15.1"
-graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.10.0, graphql-language-service-parser@^1.9.0:
- version "1.9.0"
- resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.9.0.tgz#79af21294119a0a7e81b6b994a1af36833bab724"
- integrity sha512-B5xPZLbBmIp0kHvpY1Z35I5DtPoDK9wGxQVRDIzcBaiIvAmlTrDvjo3bu7vKREdjFbYKvWNgrEWENuprMbF17Q==
+graphql-language-service-parser@^1.10.0:
+ version "1.10.0"
+ resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.10.0.tgz#116f4be849754f6afb4c196421a43fe96d87b278"
+ integrity sha512-cLExv0EjqT2hsKdwVTPmKU6eMfjZAjxqywgCPnWD48eJn6tyuePMyG7ye+jpX1PRPPx/cDHfFJGf8sUclchvng==
dependencies:
graphql-language-service-types "^1.8.0"
@@ -15618,13 +15618,10 @@ graphql-language-service-types@^1.8.2:
resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.2.tgz#50ae56f69cc24fcfc3daa129b68b0eb9421e8578"
integrity sha512-Sj07RHnMwAhEvAt7Jdt1l/x56ZpoNh+V6g+T58CF6GiYqI5l4vXqqRB4d4xHDcNQX98GpJfnf3o8BqPgP3C5Sw==
-graphql-language-service-utils@^2.5.1:
- version "2.5.1"
- resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.1.tgz#832ad4b0a9da03fdded756932c27e057ccf71302"
- integrity sha512-Lzz723cYrYlVN4WVzIyFGg3ogoe+QYAIBfdtDboiIILoy0FTmqbyC2TOErqbmWKqO4NK9xDA95cSRFbWiHYj0g==
- dependencies:
- graphql-language-service-types "^1.8.0"
- nullthrows "^1.0.0"
+graphql-language-service-types@^1.8.3:
+ version "1.8.3"
+ resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.3.tgz#d7d688d74c122c4d9cc4cceae761a1f2a3c396a1"
+ integrity sha512-m+RHnlGkKDcesW/gC4M7I2pSmWJB84uWS6LtnjplO/07JN312nJCJYCwV/DBny2m1fmSOxN7H/o+JW0l56KwBA==
graphql-language-service-utils@^2.5.3:
version "2.5.3"
@@ -15634,6 +15631,14 @@ graphql-language-service-utils@^2.5.3:
graphql-language-service-types "^1.8.0"
nullthrows "^1.0.0"
+graphql-language-service-utils@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.6.0.tgz#d04904641248167ccbb381d8705ba97daa784954"
+ integrity sha512-idqwmbREixhDuQMcYp8WH0btQT02xZny8MO/HduNTVjnPrmTYnZUbpZ9AejdflmaKoS0o8nNvgXQ0GpIOzbG5g==
+ dependencies:
+ graphql-language-service-types "^1.8.3"
+ nullthrows "^1.0.0"
+
graphql-language-service@^3.1.6:
version "3.2.0"
resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.2.0.tgz#e0eb6d5dea2cab92549a253d7a6b4fa0cce178b7"