Merge pull request #22612 from Bonial-International-GmbH/pjungermann/catalog/remove-module-bitbucket
chore(catalog): remove deprecated catalog-backend-module-bitbucket plugin
This commit is contained in:
@@ -1 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
# Catalog Backend Module for Bitbucket
|
||||
|
||||
This is an extension module to the plugin-catalog-backend plugin, providing extensions targeted at Bitbucket offerings.
|
||||
|
||||
## Getting started
|
||||
|
||||
See [Backstage documentation](https://backstage.io/docs/integrations/bitbucket/discovery) for details on how to install
|
||||
and configure the plugin.
|
||||
@@ -1,47 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-catalog-backend-module-bitbucket"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BitbucketIntegration } from '@backstage/integration';
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-node';
|
||||
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
|
||||
import { CatalogProcessorResult } from '@backstage/plugin-catalog-node';
|
||||
import { Config } from '@backstage/config';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-node';
|
||||
import { Logger } from 'winston';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export class BitbucketDiscoveryProcessor implements CatalogProcessor {
|
||||
constructor(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
parser?: BitbucketRepositoryParser;
|
||||
logger: Logger;
|
||||
});
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: {
|
||||
parser?: BitbucketRepositoryParser;
|
||||
logger: Logger;
|
||||
},
|
||||
): BitbucketDiscoveryProcessor;
|
||||
// (undocumented)
|
||||
getProcessorName(): string;
|
||||
// (undocumented)
|
||||
readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export type BitbucketRepositoryParser = (options: {
|
||||
integration: BitbucketIntegration;
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
logger: Logger;
|
||||
}) => AsyncIterable<CatalogProcessorResult>;
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-catalog-backend-module-bitbucket
|
||||
title: '@backstage/plugin-catalog-backend-module-bitbucket'
|
||||
description: A Backstage catalog backend module that helps integrate towards Bitbucket
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-backend-plugin-module
|
||||
owner: catalog-maintainers
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-backend-module-bitbucket",
|
||||
"description": "A Backstage catalog backend module that helps integrate towards Bitbucket",
|
||||
"version": "0.2.25-next.2",
|
||||
"deprecated": true,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "backend-plugin-module"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/catalog-backend-module-bitbucket"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"clean": "backstage-cli package clean",
|
||||
"start": "backstage-cli package start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/plugin-bitbucket-cloud-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-node": "workspace:^",
|
||||
"node-fetch": "^2.6.7",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"msw": "^1.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,415 +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 { Config } from '@backstage/config';
|
||||
import {
|
||||
BitbucketIntegration,
|
||||
ScmIntegrationRegistry,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
BitbucketCloudClient,
|
||||
Models,
|
||||
} from '@backstage/plugin-bitbucket-cloud-common';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorEmit,
|
||||
LocationSpec,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
BitbucketRepository,
|
||||
BitbucketRepositoryParser,
|
||||
BitbucketServerClient,
|
||||
defaultRepositoryParser,
|
||||
paginated,
|
||||
} from './lib';
|
||||
|
||||
const DEFAULT_BRANCH = 'master';
|
||||
const DEFAULT_CATALOG_LOCATION = '/catalog-info.yaml';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead.
|
||||
*/
|
||||
export class BitbucketDiscoveryProcessor implements CatalogProcessor {
|
||||
private readonly integrations: ScmIntegrationRegistry;
|
||||
private readonly parser: BitbucketRepositoryParser;
|
||||
private readonly logger: Logger;
|
||||
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options: {
|
||||
parser?: BitbucketRepositoryParser;
|
||||
logger: Logger;
|
||||
},
|
||||
) {
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
|
||||
return new BitbucketDiscoveryProcessor({
|
||||
...options,
|
||||
integrations,
|
||||
});
|
||||
}
|
||||
|
||||
constructor(options: {
|
||||
integrations: ScmIntegrationRegistry;
|
||||
parser?: BitbucketRepositoryParser;
|
||||
logger: Logger;
|
||||
}) {
|
||||
this.integrations = options.integrations;
|
||||
this.parser = options.parser || defaultRepositoryParser;
|
||||
this.logger = options.logger;
|
||||
this.logger.warn(
|
||||
'Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
getProcessorName(): string {
|
||||
return 'BitbucketDiscoveryProcessor';
|
||||
}
|
||||
|
||||
async readLocation(
|
||||
location: LocationSpec,
|
||||
_optional: boolean,
|
||||
emit: CatalogProcessorEmit,
|
||||
): Promise<boolean> {
|
||||
if (location.type !== 'bitbucket-discovery') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const integration = this.integrations.bitbucket.byUrl(location.target);
|
||||
if (!integration) {
|
||||
throw new Error(
|
||||
`There is no Bitbucket integration that matches ${location.target}. Please add a configuration entry for it under integrations.bitbucket`,
|
||||
);
|
||||
}
|
||||
|
||||
const startTimestamp = Date.now();
|
||||
this.logger.info(
|
||||
`Reading ${integration.config.host} repositories from ${location.target}`,
|
||||
);
|
||||
|
||||
const processOptions: ProcessOptions = {
|
||||
emit,
|
||||
integration,
|
||||
location,
|
||||
};
|
||||
|
||||
const isBitbucketCloud = integration.config.host === 'bitbucket.org';
|
||||
const { scanned, matches } = isBitbucketCloud
|
||||
? await this.processCloudRepositories(processOptions)
|
||||
: await this.processOrganizationRepositories(processOptions);
|
||||
|
||||
const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);
|
||||
this.logger.debug(
|
||||
`Read ${scanned} ${integration.config.host} repositories (${matches} matching the pattern) in ${duration} seconds`,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async processCloudRepositories(
|
||||
options: ProcessOptions,
|
||||
): Promise<ResultSummary> {
|
||||
const { location, integration, emit } = options;
|
||||
const client = BitbucketCloudClient.fromConfig(integration.config);
|
||||
|
||||
const { searchEnabled } = parseBitbucketCloudUrl(location.target);
|
||||
|
||||
const result = searchEnabled
|
||||
? await searchBitbucketCloudLocations(client, location.target)
|
||||
: await readBitbucketCloudLocations(client, location.target);
|
||||
|
||||
for (const locationTarget of result.matches) {
|
||||
for await (const entity of this.parser({
|
||||
integration,
|
||||
target: locationTarget,
|
||||
presence: searchEnabled ? 'required' : 'optional',
|
||||
logger: this.logger,
|
||||
})) {
|
||||
emit(entity);
|
||||
}
|
||||
}
|
||||
return {
|
||||
matches: result.matches.length,
|
||||
scanned: result.scanned,
|
||||
};
|
||||
}
|
||||
|
||||
private async processOrganizationRepositories(
|
||||
options: ProcessOptions,
|
||||
): Promise<ResultSummary> {
|
||||
const { location, integration, emit } = options;
|
||||
const { catalogPath: requestedCatalogPath } = parseUrl(location.target);
|
||||
const catalogPath = requestedCatalogPath
|
||||
? `/${requestedCatalogPath}`
|
||||
: DEFAULT_CATALOG_LOCATION;
|
||||
|
||||
const client = new BitbucketServerClient({
|
||||
config: integration.config,
|
||||
});
|
||||
|
||||
const result = await readBitbucketOrg(client, location.target);
|
||||
for (const repository of result.matches) {
|
||||
for await (const entity of this.parser({
|
||||
integration,
|
||||
target: `${repository.links.self[0].href}${catalogPath}`,
|
||||
logger: this.logger,
|
||||
})) {
|
||||
emit(entity);
|
||||
}
|
||||
}
|
||||
return {
|
||||
matches: result.matches.length,
|
||||
scanned: result.scanned,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function readBitbucketOrg(
|
||||
client: BitbucketServerClient,
|
||||
target: string,
|
||||
): Promise<Result<BitbucketRepository>> {
|
||||
const { projectSearchPath, repoSearchPath } = parseUrl(target);
|
||||
const projects = paginated(options => client.listProjects(options));
|
||||
const result: Result<BitbucketRepository> = {
|
||||
scanned: 0,
|
||||
matches: [],
|
||||
};
|
||||
|
||||
for await (const project of projects) {
|
||||
if (!projectSearchPath.test(project.key)) {
|
||||
continue;
|
||||
}
|
||||
const repositories = paginated(options =>
|
||||
client.listRepositories(project.key, options),
|
||||
);
|
||||
for await (const repository of repositories) {
|
||||
result.scanned++;
|
||||
if (repoSearchPath.test(repository.slug)) {
|
||||
result.matches.push(repository);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function searchBitbucketCloudLocations(
|
||||
client: BitbucketCloudClient,
|
||||
target: string,
|
||||
): Promise<Result<string>> {
|
||||
const {
|
||||
workspacePath,
|
||||
catalogPath: requestedCatalogPath,
|
||||
projectSearchPath,
|
||||
repoSearchPath,
|
||||
} = parseBitbucketCloudUrl(target);
|
||||
|
||||
const result: Result<string> = {
|
||||
scanned: 0,
|
||||
matches: [],
|
||||
};
|
||||
|
||||
const catalogPath = requestedCatalogPath
|
||||
? requestedCatalogPath
|
||||
: DEFAULT_CATALOG_LOCATION;
|
||||
const catalogFilename = catalogPath.substring(
|
||||
catalogPath.lastIndexOf('/') + 1,
|
||||
);
|
||||
|
||||
// load all fields relevant for creating refs later, but not more
|
||||
const fields = [
|
||||
// exclude code/content match details
|
||||
'-values.content_matches',
|
||||
// include/add relevant repository details
|
||||
'+values.file.commit.repository.mainbranch.name',
|
||||
'+values.file.commit.repository.project.key',
|
||||
'+values.file.commit.repository.slug',
|
||||
// remove irrelevant links
|
||||
'-values.*.links',
|
||||
'-values.*.*.links',
|
||||
'-values.*.*.*.links',
|
||||
// ...except the one we need
|
||||
'+values.file.commit.repository.links.html.href',
|
||||
].join(',');
|
||||
const query = `"${catalogFilename}" path:${catalogPath}`;
|
||||
const searchResults = client
|
||||
.searchCode(workspacePath, query, { fields })
|
||||
.iterateResults();
|
||||
|
||||
for await (const searchResult of searchResults) {
|
||||
// not a file match, but a code match
|
||||
if (searchResult.path_matches!.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const repository = searchResult.file!.commit!.repository!;
|
||||
if (!matchesPostFilters(repository, projectSearchPath, repoSearchPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const repoUrl = repository.links!.html!.href;
|
||||
const branch = repository.mainbranch?.name ?? DEFAULT_BRANCH;
|
||||
const filePath = searchResult.file!.path;
|
||||
const location = `${repoUrl}/src/${branch}/${filePath}`;
|
||||
|
||||
result.matches.push(location);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function readBitbucketCloudLocations(
|
||||
client: BitbucketCloudClient,
|
||||
target: string,
|
||||
): Promise<Result<string>> {
|
||||
const { catalogPath: requestedCatalogPath } = parseBitbucketCloudUrl(target);
|
||||
const catalogPath = requestedCatalogPath
|
||||
? `/${requestedCatalogPath}`
|
||||
: DEFAULT_CATALOG_LOCATION;
|
||||
|
||||
return readBitbucketCloud(client, target).then(result => {
|
||||
const matches = result.matches.map(repository => {
|
||||
const branch = repository.mainbranch?.name ?? DEFAULT_BRANCH;
|
||||
return `${repository.links!.html!.href}/src/${branch}${catalogPath}`;
|
||||
});
|
||||
|
||||
return {
|
||||
scanned: result.scanned,
|
||||
matches,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function readBitbucketCloud(
|
||||
client: BitbucketCloudClient,
|
||||
target: string,
|
||||
): Promise<Result<Models.Repository>> {
|
||||
const {
|
||||
workspacePath,
|
||||
queryParam: q,
|
||||
projectSearchPath,
|
||||
repoSearchPath,
|
||||
} = parseBitbucketCloudUrl(target);
|
||||
|
||||
const repositories = client
|
||||
.listRepositoriesByWorkspace(workspacePath, { q })
|
||||
.iterateResults();
|
||||
const result: Result<Models.Repository> = {
|
||||
scanned: 0,
|
||||
matches: [],
|
||||
};
|
||||
|
||||
for await (const repository of repositories) {
|
||||
result.scanned++;
|
||||
if (matchesPostFilters(repository, projectSearchPath, repoSearchPath)) {
|
||||
result.matches.push(repository);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function matchesPostFilters(
|
||||
repository: Models.Repository,
|
||||
projectSearchPath: RegExp | undefined,
|
||||
repoSearchPath: RegExp | undefined,
|
||||
): boolean {
|
||||
return (
|
||||
(!projectSearchPath || projectSearchPath.test(repository.project!.key!)) &&
|
||||
(!repoSearchPath || repoSearchPath.test(repository.slug!))
|
||||
);
|
||||
}
|
||||
|
||||
function parseUrl(urlString: string): {
|
||||
projectSearchPath: RegExp;
|
||||
repoSearchPath: RegExp;
|
||||
catalogPath: string;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const indexOfProjectSegment =
|
||||
url.pathname.toLowerCase().indexOf('/projects/') + 1;
|
||||
const path = url.pathname.slice(indexOfProjectSegment).split('/');
|
||||
|
||||
// /projects/backstage/repos/techdocs-*/catalog-info.yaml
|
||||
if (path.length > 3 && path[1].length && path[3].length) {
|
||||
return {
|
||||
projectSearchPath: escapeRegExp(decodeURIComponent(path[1])),
|
||||
repoSearchPath: escapeRegExp(decodeURIComponent(path[3])),
|
||||
catalogPath: decodeURIComponent(path.slice(4).join('/') + url.search),
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Failed to parse ${urlString}`);
|
||||
}
|
||||
|
||||
function readPathParameters(pathParts: string[]): Map<string, string> {
|
||||
const vals: Record<string, any> = {};
|
||||
for (let i = 0; i + 1 < pathParts.length; i += 2) {
|
||||
vals[pathParts[i]] = decodeURIComponent(pathParts[i + 1]);
|
||||
}
|
||||
return new Map<string, string>(Object.entries(vals));
|
||||
}
|
||||
|
||||
function parseBitbucketCloudUrl(urlString: string): {
|
||||
workspacePath: string;
|
||||
catalogPath?: string;
|
||||
projectSearchPath?: RegExp;
|
||||
repoSearchPath?: RegExp;
|
||||
queryParam?: string;
|
||||
searchEnabled: boolean;
|
||||
} {
|
||||
const url = new URL(urlString);
|
||||
const pathMap = readPathParameters(url.pathname.slice(1).split('/'));
|
||||
const query = url.searchParams;
|
||||
|
||||
if (!pathMap.has('workspaces')) {
|
||||
throw new Error(`Failed to parse workspace from ${urlString}`);
|
||||
}
|
||||
|
||||
return {
|
||||
workspacePath: pathMap.get('workspaces')!,
|
||||
projectSearchPath: pathMap.has('projects')
|
||||
? escapeRegExp(pathMap.get('projects')!)
|
||||
: undefined,
|
||||
repoSearchPath: pathMap.has('repos')
|
||||
? escapeRegExp(pathMap.get('repos')!)
|
||||
: undefined,
|
||||
catalogPath: query.get('catalogPath') || undefined,
|
||||
queryParam: query.get('q') || undefined,
|
||||
searchEnabled: query.get('search')?.toLowerCase() === 'true',
|
||||
};
|
||||
}
|
||||
|
||||
function escapeRegExp(str: string): RegExp {
|
||||
return new RegExp(`^${str.replace(/\*/g, '.*')}$`);
|
||||
}
|
||||
|
||||
type ProcessOptions = {
|
||||
integration: BitbucketIntegration;
|
||||
location: LocationSpec;
|
||||
emit: CatalogProcessorEmit;
|
||||
};
|
||||
|
||||
type Result<T> = {
|
||||
scanned: number;
|
||||
matches: T[];
|
||||
};
|
||||
|
||||
type ResultSummary = {
|
||||
scanned: number;
|
||||
matches: number;
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Backstage catalog backend module that helps integrate towards Bitbucket
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor';
|
||||
export type { BitbucketRepositoryParser } from './lib/BitbucketRepositoryParser';
|
||||
@@ -1,44 +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 { processingResult } from '@backstage/plugin-catalog-node';
|
||||
import { defaultRepositoryParser } from './BitbucketRepositoryParser';
|
||||
|
||||
describe('BitbucketRepositoryParser', () => {
|
||||
describe('defaultRepositoryParser', () => {
|
||||
it('emits location', async () => {
|
||||
const browseUrl =
|
||||
'https://bitbucket.mycompany.com/projects/project-key/repos/repo-slug/browse';
|
||||
const path = '/catalog-info.yaml';
|
||||
const expected = [
|
||||
processingResult.location({
|
||||
type: 'url',
|
||||
target: `${browseUrl}${path}`,
|
||||
presence: 'optional',
|
||||
}),
|
||||
];
|
||||
const actual = defaultRepositoryParser({
|
||||
target: `${browseUrl}${path}`,
|
||||
});
|
||||
|
||||
let i = 0;
|
||||
for await (const entity of actual) {
|
||||
expect(entity).toStrictEqual(expected[i]);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,51 +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 { BitbucketIntegration } from '@backstage/integration';
|
||||
import {
|
||||
CatalogProcessorResult,
|
||||
processingResult,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
/**
|
||||
* A custom callback that reacts to finding a repository by yielding processing
|
||||
* results.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Please migrate to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` or `@backstage/plugin-catalog-backend-module-bitbucket-server` instead.
|
||||
*/
|
||||
export type BitbucketRepositoryParser = (options: {
|
||||
integration: BitbucketIntegration;
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
logger: Logger;
|
||||
}) => AsyncIterable<CatalogProcessorResult>;
|
||||
|
||||
export const defaultRepositoryParser =
|
||||
async function* defaultRepositoryParser(options: {
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
}) {
|
||||
yield processingResult.location({
|
||||
type: 'url',
|
||||
target: options.target,
|
||||
// Not all locations may actually exist, since the user defined them as a wildcard pattern.
|
||||
// Thus, we emit them as optional and let the downstream processor find them while not outputting
|
||||
// an error if it couldn't.
|
||||
presence: options.presence ?? 'optional',
|
||||
});
|
||||
};
|
||||
@@ -1,100 +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 {
|
||||
BitbucketIntegrationConfig,
|
||||
getBitbucketRequestOptions,
|
||||
} from '@backstage/integration';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
export class BitbucketServerClient {
|
||||
private readonly config: BitbucketIntegrationConfig;
|
||||
|
||||
constructor(options: { config: BitbucketIntegrationConfig }) {
|
||||
this.config = options.config;
|
||||
}
|
||||
|
||||
async listProjects(options?: ListOptions): Promise<PagedResponse<any>> {
|
||||
return this.pagedRequest(`${this.config.apiBaseUrl}/projects`, options);
|
||||
}
|
||||
|
||||
async listRepositories(
|
||||
projectKey: string,
|
||||
options?: ListOptions,
|
||||
): Promise<PagedResponse<any>> {
|
||||
return this.pagedRequest(
|
||||
`${this.config.apiBaseUrl}/projects/${encodeURIComponent(
|
||||
projectKey,
|
||||
)}/repos`,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
private async pagedRequest(
|
||||
endpoint: string,
|
||||
options?: ListOptions,
|
||||
): Promise<PagedResponse<any>> {
|
||||
const request = new URL(endpoint);
|
||||
for (const key in options) {
|
||||
if (options[key]) {
|
||||
request.searchParams.append(key, options[key]!.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
request.toString(),
|
||||
getBitbucketRequestOptions(this.config),
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Unexpected response when fetching ${request.toString()}. Expected 200 but got ${
|
||||
response.status
|
||||
} - ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
return response.json() as Promise<PagedResponse<any>>;
|
||||
}
|
||||
}
|
||||
|
||||
export type ListOptions = {
|
||||
[key: string]: number | undefined;
|
||||
limit?: number | undefined;
|
||||
start?: number | undefined;
|
||||
};
|
||||
|
||||
export type PagedResponse<T> = {
|
||||
size: number;
|
||||
limit: number;
|
||||
start: number;
|
||||
isLastPage: boolean;
|
||||
values: T[];
|
||||
nextPageStart: number;
|
||||
};
|
||||
|
||||
export async function* paginated(
|
||||
request: (options: ListOptions) => Promise<PagedResponse<any>>,
|
||||
options?: ListOptions,
|
||||
) {
|
||||
const opts = options || { start: 0 };
|
||||
let res;
|
||||
do {
|
||||
res = await request(opts);
|
||||
opts.start = res.nextPageStart;
|
||||
for (const item of res.values) {
|
||||
yield item;
|
||||
}
|
||||
} while (!res.isLastPage);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { defaultRepositoryParser } from './BitbucketRepositoryParser';
|
||||
export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser';
|
||||
export { BitbucketServerClient, paginated } from './BitbucketServerClient';
|
||||
export type { PagedResponse } from './BitbucketServerClient';
|
||||
export type { BitbucketRepository } from './types';
|
||||
@@ -1,31 +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.
|
||||
*/
|
||||
|
||||
type BitbucketRepositoryBase = {
|
||||
project: {
|
||||
key: string;
|
||||
};
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type BitbucketRepository = BitbucketRepositoryBase & {
|
||||
links: Record<
|
||||
string,
|
||||
{
|
||||
href: string;
|
||||
}[]
|
||||
>;
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 {};
|
||||
@@ -5419,23 +5419,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-catalog-backend-module-bitbucket@workspace:plugins/catalog-backend-module-bitbucket":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-bitbucket@workspace:plugins/catalog-backend-module-bitbucket"
|
||||
dependencies:
|
||||
"@backstage/backend-common": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/integration": "workspace:^"
|
||||
"@backstage/plugin-bitbucket-cloud-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-node": "workspace:^"
|
||||
msw: ^1.0.0
|
||||
node-fetch: ^2.6.7
|
||||
winston: ^3.2.1
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-catalog-backend-module-gcp@workspace:plugins/catalog-backend-module-gcp":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-gcp@workspace:plugins/catalog-backend-module-gcp"
|
||||
|
||||
Reference in New Issue
Block a user