remove all regular deprecated exports except the builder

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2025-04-25 16:11:13 +02:00
parent 6c9b88e70c
commit 90ab044afe
12 changed files with 104 additions and 1314 deletions
+57
View File
@@ -0,0 +1,57 @@
---
'@backstage/plugin-catalog-backend': major
---
**BREAKING**: Removed all deprecated exports.
The following removed exports are available from `@backstage/plugin-catalog-node`:
- `locationSpecToMetadataName`
- `locationSpecToLocationEntity`
- `processingResult`
- `EntitiesSearchFilter`
- `EntityFilter`
- `DeferredEntity`
- `EntityRelationSpec`
- `CatalogProcessor`
- `CatalogProcessorParser`
- `CatalogProcessorCache`
- `CatalogProcessorEmit`
- `CatalogProcessorLocationResult`
- `CatalogProcessorEntityResult`
- `CatalogProcessorRelationResult`
- `CatalogProcessorErrorResult`
- `CatalogProcessorRefreshKeysResult`
- `CatalogProcessorResult`
- `EntityProvider`
- `EntityProviderConnection`
- `EntityProviderMutation`
- `AnalyzeOptions`
- `LocationAnalyzer`
- `ScmLocationAnalyzer`
- `PlaceholderResolver`
- `PlaceholderResolverParams`
- `PlaceholderResolverRead`
- `PlaceholderResolverResolveUrl`
The following removed exports are available from `@backstage/plugin-catalog-common`:
- `LocationSpec`
- `AnalyzeLocationRequest`
- `AnalyzeLocationResponse`
- `AnalyzeLocationExistingEntity`
- `AnalyzeLocationGenerateEntity`
- `AnalyzeLocationEntityField`
The following removed exports are instead implemented in the new backend system by `@backstage/plugin-search-backend-module-catalog`:
- `defaultCatalogCollatorEntityTransformer`
- `CatalogCollatorEntityTransformer`
- `DefaultCatalogCollator`
The following exports are removed without a direct replacement:
- `DefaultCatalogCollatorFactory`
- `DefaultCatalogCollatorFactoryOptions`
- `LocationEntityProcessor`
- `LocationEntityProcessorOptions`
-2
View File
@@ -74,8 +74,6 @@
"@backstage/plugin-events-node": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^",
"@backstage/plugin-permission-node": "workspace:^",
"@backstage/plugin-search-backend-module-catalog": "workspace:^",
"@backstage/plugin-search-common": "workspace:^",
"@backstage/types": "workspace:^",
"@opentelemetry/api": "^1.9.0",
"@types/express": "^4.17.6",
+47 -291
View File
@@ -3,49 +3,26 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AnalyzeLocationEntityField as AnalyzeLocationEntityField_2 } from '@backstage/plugin-catalog-common';
import { AnalyzeLocationExistingEntity as AnalyzeLocationExistingEntity_2 } from '@backstage/plugin-catalog-common';
import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from '@backstage/plugin-catalog-common';
import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common';
import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common';
import { AnalyzeOptions as AnalyzeOptions_2 } from '@backstage/plugin-catalog-node';
import { AuditorService } from '@backstage/backend-plugin-api';
import { AuthService } from '@backstage/backend-plugin-api';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { CatalogApi } from '@backstage/catalog-client';
import { CatalogCollatorEntityTransformer as CatalogCollatorEntityTransformer_2 } from '@backstage/plugin-search-backend-module-catalog';
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
import { CatalogProcessor as CatalogProcessor_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorCache as CatalogProcessorCache_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorEmit as CatalogProcessorEmit_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorEntityResult as CatalogProcessorEntityResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorErrorResult as CatalogProcessorErrorResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorLocationResult as CatalogProcessorLocationResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorParser as CatalogProcessorParser_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorRefreshKeysResult as CatalogProcessorRefreshKeysResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorRelationResult as CatalogProcessorRelationResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessorResult as CatalogProcessorResult_2 } from '@backstage/plugin-catalog-node';
import { CatalogProcessor } from '@backstage/plugin-catalog-node';
import { CatalogProcessorCache } from '@backstage/plugin-catalog-node';
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
import { CatalogProcessorParser } from '@backstage/plugin-catalog-node';
import { CatalogProcessorResult } from '@backstage/plugin-catalog-node';
import { Config } from '@backstage/config';
import { DatabaseService } from '@backstage/backend-plugin-api';
import { DeferredEntity as DeferredEntity_2 } from '@backstage/plugin-catalog-node';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
import { EntitiesSearchFilter as EntitiesSearchFilter_2 } from '@backstage/plugin-catalog-node';
import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node';
import { Entity } from '@backstage/catalog-model';
import { EntityFilter as EntityFilter_2 } from '@backstage/plugin-catalog-node';
import { EntityPolicy } from '@backstage/catalog-model';
import { EntityProvider as EntityProvider_2 } from '@backstage/plugin-catalog-node';
import { EntityProviderConnection as EntityProviderConnection_2 } from '@backstage/plugin-catalog-node';
import { EntityProviderMutation as EntityProviderMutation_2 } from '@backstage/plugin-catalog-node';
import { EntityRelationSpec as EntityRelationSpec_2 } from '@backstage/plugin-catalog-node';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { EventBroker } from '@backstage/plugin-events-node';
import { EventsService } from '@backstage/plugin-events-node';
import { GetEntitiesRequest } from '@backstage/catalog-client';
import { HttpAuthService } from '@backstage/backend-plugin-api';
import { LocationAnalyzer as LocationAnalyzer_2 } from '@backstage/plugin-catalog-node';
import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common';
import { locationSpecToLocationEntity as locationSpecToLocationEntity_2 } from '@backstage/plugin-catalog-node';
import { locationSpecToMetadataName as locationSpecToMetadataName_2 } from '@backstage/plugin-catalog-node';
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { LoggerService } from '@backstage/backend-plugin-api';
import { Permission } from '@backstage/plugin-permission-common';
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
@@ -53,54 +30,31 @@ import { PermissionRule } from '@backstage/plugin-permission-node';
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import { PermissionsRegistryService } from '@backstage/backend-plugin-api';
import { PermissionsService } from '@backstage/backend-plugin-api';
import { PlaceholderResolver as PlaceholderResolver_2 } from '@backstage/plugin-catalog-node';
import { PlaceholderResolverParams as PlaceholderResolverParams_2 } from '@backstage/plugin-catalog-node';
import { PlaceholderResolverRead as PlaceholderResolverRead_2 } from '@backstage/plugin-catalog-node';
import { PlaceholderResolverResolveUrl as PlaceholderResolverResolveUrl_2 } from '@backstage/plugin-catalog-node';
import { Readable } from 'stream';
import { PlaceholderResolver } from '@backstage/plugin-catalog-node';
import { RootConfigService } from '@backstage/backend-plugin-api';
import { Router } from 'express';
import { SchedulerService } from '@backstage/backend-plugin-api';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { ScmLocationAnalyzer as ScmLocationAnalyzer_2 } from '@backstage/plugin-catalog-node';
import { TokenManager } from '@backstage/backend-common';
import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node';
import { UrlReaderService } from '@backstage/backend-plugin-api';
import { Validators } from '@backstage/catalog-model';
// @public @deprecated
export type AnalyzeLocationEntityField = AnalyzeLocationEntityField_2;
// @public @deprecated
export type AnalyzeLocationExistingEntity = AnalyzeLocationExistingEntity_2;
// @public @deprecated
export type AnalyzeLocationGenerateEntity = AnalyzeLocationGenerateEntity_2;
// @public @deprecated (undocumented)
export type AnalyzeLocationRequest = AnalyzeLocationRequest_2;
// @public @deprecated (undocumented)
export type AnalyzeLocationResponse = AnalyzeLocationResponse_2;
// @public @deprecated (undocumented)
export type AnalyzeOptions = AnalyzeOptions_2;
// @public (undocumented)
export class AnnotateLocationEntityProcessor implements CatalogProcessor_2 {
export class AnnotateLocationEntityProcessor implements CatalogProcessor {
constructor(options: { integrations: ScmIntegrationRegistry });
// (undocumented)
getProcessorName(): string;
// (undocumented)
preProcessEntity(
entity: Entity,
location: LocationSpec_2,
_: CatalogProcessorEmit_2,
originLocation: LocationSpec_2,
location: LocationSpec,
_: CatalogProcessorEmit,
originLocation: LocationSpec,
): Promise<Entity>;
}
// @public (undocumented)
export class AnnotateScmSlugEntityProcessor implements CatalogProcessor_2 {
export class AnnotateScmSlugEntityProcessor implements CatalogProcessor {
constructor(opts: {
scmIntegrationRegistry: ScmIntegrationRegistry;
kinds?: string[];
@@ -115,18 +69,18 @@ export class AnnotateScmSlugEntityProcessor implements CatalogProcessor_2 {
// (undocumented)
getProcessorName(): string;
// (undocumented)
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
preProcessEntity(entity: Entity, location: LocationSpec): Promise<Entity>;
}
// @public (undocumented)
export class BuiltinKindsEntityProcessor implements CatalogProcessor_2 {
export class BuiltinKindsEntityProcessor implements CatalogProcessor {
// (undocumented)
getProcessorName(): string;
// (undocumented)
postProcessEntity(
entity: Entity,
_location: LocationSpec_2,
emit: CatalogProcessorEmit_2,
_location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity>;
// (undocumented)
validateEntityKind(entity: Entity): Promise<boolean>;
@@ -144,10 +98,10 @@ export class CatalogBuilder {
...policies: Array<EntityPolicy | Array<EntityPolicy>>
): CatalogBuilder;
addEntityProvider(
...providers: Array<EntityProvider_2 | Array<EntityProvider_2>>
...providers: Array<EntityProvider | Array<EntityProvider>>
): CatalogBuilder;
addLocationAnalyzers(
...analyzers: Array<ScmLocationAnalyzer_2 | Array<ScmLocationAnalyzer_2>>
...analyzers: Array<ScmLocationAnalyzer | Array<ScmLocationAnalyzer>>
): CatalogBuilder;
addPermissionRules(
...permissionRules: Array<
@@ -156,24 +110,24 @@ export class CatalogBuilder {
): this;
addPermissions(...permissions: Array<Permission | Array<Permission>>): this;
addProcessor(
...processors: Array<CatalogProcessor_2 | Array<CatalogProcessor_2>>
...processors: Array<CatalogProcessor | Array<CatalogProcessor>>
): CatalogBuilder;
build(): Promise<{
processingEngine: CatalogProcessingEngine;
router: Router;
}>;
static create(env: CatalogEnvironment): CatalogBuilder;
getDefaultProcessors(): CatalogProcessor_2[];
getDefaultProcessors(): CatalogProcessor[];
replaceEntityPolicies(policies: EntityPolicy[]): CatalogBuilder;
replaceProcessors(processors: CatalogProcessor_2[]): CatalogBuilder;
replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder;
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
setEntityDataParser(parser: CatalogProcessorParser_2): CatalogBuilder;
setEntityDataParser(parser: CatalogProcessorParser): CatalogBuilder;
setEventBroker(broker: EventBroker | EventsService): CatalogBuilder;
setFieldFormatValidators(validators: Partial<Validators>): CatalogBuilder;
setLocationAnalyzer(locationAnalyzer: LocationAnalyzer_2): CatalogBuilder;
setLocationAnalyzer(locationAnalyzer: LocationAnalyzer): CatalogBuilder;
setPlaceholderResolver(
key: string,
resolver: PlaceholderResolver_2,
resolver: PlaceholderResolver,
): CatalogBuilder;
setProcessingInterval(
processingInterval: ProcessingIntervalFunction,
@@ -189,10 +143,6 @@ export class CatalogBuilder {
useLegacySingleProcessorValidation(): this;
}
// @public @deprecated (undocumented)
export type CatalogCollatorEntityTransformer =
CatalogCollatorEntityTransformer_2;
// @public @deprecated (undocumented)
export type CatalogEnvironment = {
logger: LoggerService;
@@ -211,7 +161,7 @@ export type CatalogEnvironment = {
// @public
export type CatalogPermissionRuleInput<
TParams extends PermissionRuleParams = PermissionRuleParams,
> = PermissionRule<Entity, EntitiesSearchFilter_2, 'catalog-entity', TParams>;
> = PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
// @public
const catalogPlugin: BackendFeature;
@@ -225,39 +175,8 @@ export interface CatalogProcessingEngine {
stop(): Promise<void>;
}
// @public @deprecated (undocumented)
export type CatalogProcessor = CatalogProcessor_2;
// @public @deprecated (undocumented)
export type CatalogProcessorCache = CatalogProcessorCache_2;
// @public @deprecated (undocumented)
export type CatalogProcessorEmit = CatalogProcessorEmit_2;
// @public @deprecated (undocumented)
export type CatalogProcessorEntityResult = CatalogProcessorEntityResult_2;
// @public @deprecated (undocumented)
export type CatalogProcessorErrorResult = CatalogProcessorErrorResult_2;
// @public @deprecated (undocumented)
export type CatalogProcessorLocationResult = CatalogProcessorLocationResult_2;
// @public @deprecated (undocumented)
export type CatalogProcessorParser = CatalogProcessorParser_2;
// @public @deprecated (undocumented)
export type CatalogProcessorRefreshKeysResult =
CatalogProcessorRefreshKeysResult_2;
// @public @deprecated (undocumented)
export type CatalogProcessorRelationResult = CatalogProcessorRelationResult_2;
// @public @deprecated (undocumented)
export type CatalogProcessorResult = CatalogProcessorResult_2;
// @public (undocumented)
export class CodeOwnersProcessor implements CatalogProcessor_2 {
export class CodeOwnersProcessor implements CatalogProcessor {
constructor(options: {
integrations: ScmIntegrationRegistry;
logger: LoggerService;
@@ -274,7 +193,7 @@ export class CodeOwnersProcessor implements CatalogProcessor_2 {
// (undocumented)
getProcessorName(): string;
// (undocumented)
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
preProcessEntity(entity: Entity, location: LocationSpec): Promise<Entity>;
}
// @public
@@ -283,218 +202,55 @@ export function createRandomProcessingInterval(options: {
maxSeconds: number;
}): ProcessingIntervalFunction;
// @public @deprecated (undocumented)
export class DefaultCatalogCollator {
constructor(options: {
discovery: DiscoveryService;
tokenManager: TokenManager;
locationTemplate?: string;
filter?: GetEntitiesRequest['filter'];
catalogClient?: CatalogApi;
});
// (undocumented)
protected applyArgsToFormat(
format: string,
args: Record<string, string>,
): string;
// (undocumented)
protected readonly catalogClient: CatalogApi;
// (undocumented)
protected discovery: DiscoveryService;
// (undocumented)
execute(): Promise<CatalogEntityDocument[]>;
// (undocumented)
protected filter?: GetEntitiesRequest['filter'];
// (undocumented)
static fromConfig(
_config: Config,
options: {
discovery: DiscoveryService;
tokenManager: TokenManager;
filter?: GetEntitiesRequest['filter'];
},
): DefaultCatalogCollator;
// (undocumented)
protected locationTemplate: string;
// (undocumented)
protected tokenManager: TokenManager;
// (undocumented)
readonly type: string;
// (undocumented)
readonly visibilityPermission: Permission;
}
// @public @deprecated (undocumented)
export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer_2;
// @public @deprecated (undocumented)
export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
// (undocumented)
static fromConfig(
configRoot: Config,
options: DefaultCatalogCollatorFactoryOptions,
): DefaultCatalogCollatorFactory;
// (undocumented)
getCollator(): Promise<Readable>;
// (undocumented)
readonly type = 'software-catalog';
// (undocumented)
readonly visibilityPermission: Permission;
}
// @public @deprecated (undocumented)
export type DefaultCatalogCollatorFactoryOptions = {
auth?: AuthService;
discovery: DiscoveryService;
tokenManager?: TokenManager;
locationTemplate?: string;
filter?: GetEntitiesRequest['filter'];
batchSize?: number;
catalogClient?: CatalogApi;
entityTransformer?: CatalogCollatorEntityTransformer;
};
// @public @deprecated (undocumented)
export type DeferredEntity = DeferredEntity_2;
// @public @deprecated (undocumented)
export type EntitiesSearchFilter = EntitiesSearchFilter_2;
// @public @deprecated (undocumented)
export type EntityFilter = EntityFilter_2;
// @public @deprecated (undocumented)
export type EntityProvider = EntityProvider_2;
// @public @deprecated (undocumented)
export type EntityProviderConnection = EntityProviderConnection_2;
// @public @deprecated (undocumented)
export type EntityProviderMutation = EntityProviderMutation_2;
// @public @deprecated (undocumented)
export type EntityRelationSpec = EntityRelationSpec_2;
// @public (undocumented)
export class FileReaderProcessor implements CatalogProcessor_2 {
export class FileReaderProcessor implements CatalogProcessor {
// (undocumented)
getProcessorName(): string;
// (undocumented)
readLocation(
location: LocationSpec_2,
location: LocationSpec,
optional: boolean,
emit: CatalogProcessorEmit_2,
parser: CatalogProcessorParser_2,
emit: CatalogProcessorEmit,
parser: CatalogProcessorParser,
): Promise<boolean>;
}
// @public @deprecated (undocumented)
export type LocationAnalyzer = LocationAnalyzer_2;
// @public @deprecated
export class LocationEntityProcessor implements CatalogProcessor_2 {
constructor(options: LocationEntityProcessorOptions);
// (undocumented)
getProcessorName(): string;
// (undocumented)
postProcessEntity(
entity: Entity,
location: LocationSpec_2,
emit: CatalogProcessorEmit_2,
): Promise<Entity>;
}
// @public @deprecated (undocumented)
export type LocationEntityProcessorOptions = {
integrations: ScmIntegrationRegistry;
};
// @public @deprecated
export type LocationSpec = LocationSpec_2;
// @public @deprecated (undocumented)
export const locationSpecToLocationEntity: typeof locationSpecToLocationEntity_2;
// @public @deprecated (undocumented)
export const locationSpecToMetadataName: typeof locationSpecToMetadataName_2;
// @public (undocumented)
export function parseEntityYaml(
data: Buffer,
location: LocationSpec_2,
): Iterable<CatalogProcessorResult_2>;
location: LocationSpec,
): Iterable<CatalogProcessorResult>;
// @public
export class PlaceholderProcessor implements CatalogProcessor_2 {
export class PlaceholderProcessor implements CatalogProcessor {
constructor(options: PlaceholderProcessorOptions);
// (undocumented)
getProcessorName(): string;
// (undocumented)
preProcessEntity(
entity: Entity,
location: LocationSpec_2,
emit: CatalogProcessorEmit_2,
location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity>;
}
// @public (undocumented)
export type PlaceholderProcessorOptions = {
resolvers: Record<string, PlaceholderResolver_2>;
resolvers: Record<string, PlaceholderResolver>;
reader: UrlReaderService;
integrations: ScmIntegrationRegistry;
};
// @public @deprecated (undocumented)
export type PlaceholderResolver = PlaceholderResolver_2;
// @public @deprecated (undocumented)
export type PlaceholderResolverParams = PlaceholderResolverParams_2;
// @public @deprecated (undocumented)
export type PlaceholderResolverRead = PlaceholderResolverRead_2;
// @public @deprecated (undocumented)
export type PlaceholderResolverResolveUrl = PlaceholderResolverResolveUrl_2;
// @public
export type ProcessingIntervalFunction = () => number;
// @public @deprecated (undocumented)
export const processingResult: Readonly<{
readonly notFoundError: (
atLocation: LocationSpec_2,
message: string,
) => CatalogProcessorResult_2;
readonly inputError: (
atLocation: LocationSpec_2,
message: string,
) => CatalogProcessorResult_2;
readonly generalError: (
atLocation: LocationSpec_2,
message: string,
) => CatalogProcessorResult_2;
readonly location: (newLocation: LocationSpec_2) => CatalogProcessorResult_2;
readonly entity: (
atLocation: LocationSpec_2,
newEntity: Entity,
options?: {
locationKey?: string | null;
},
) => CatalogProcessorResult_2;
readonly relation: (spec: EntityRelationSpec_2) => CatalogProcessorResult_2;
readonly refresh: (key: string) => CatalogProcessorResult_2;
}>;
// @public @deprecated (undocumented)
export type ScmLocationAnalyzer = ScmLocationAnalyzer_2;
// @public
export function transformLegacyPolicyToProcessor(
policy: EntityPolicy,
): CatalogProcessor_2;
): CatalogProcessor;
// @public (undocumented)
export class UrlReaderProcessor implements CatalogProcessor_2 {
export class UrlReaderProcessor implements CatalogProcessor {
constructor(options: {
reader: UrlReaderService;
logger: LoggerService;
@@ -504,11 +260,11 @@ export class UrlReaderProcessor implements CatalogProcessor_2 {
getProcessorName(): string;
// (undocumented)
readLocation(
location: LocationSpec_2,
location: LocationSpec,
optional: boolean,
emit: CatalogProcessorEmit_2,
parser: CatalogProcessorParser_2,
cache: CatalogProcessorCache_2,
emit: CatalogProcessorEmit,
parser: CatalogProcessorParser,
cache: CatalogProcessorCache,
): Promise<boolean>;
}
```
-502
View File
@@ -1,502 +0,0 @@
/*
* 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 {
TokenManager,
createLegacyAuthAdapters,
} from '@backstage/backend-common';
import { AuthService, DiscoveryService } from '@backstage/backend-plugin-api';
import {
CatalogApi,
CatalogClient,
EntityFilterQuery,
GetEntitiesRequest,
} from '@backstage/catalog-client';
import {
Entity,
isGroupEntity,
isUserEntity,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import {
CatalogEntityDocument,
type AnalyzeLocationEntityField as _AnalyzeLocationEntityField,
type AnalyzeLocationExistingEntity as _AnalyzeLocationExistingEntity,
type AnalyzeLocationGenerateEntity as _AnalyzeLocationGenerateEntity,
type AnalyzeLocationRequest as _AnalyzeLocationRequest,
type AnalyzeLocationResponse as _AnalyzeLocationResponse,
type LocationSpec as _LocationSpec,
} from '@backstage/plugin-catalog-common';
import { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha';
import {
locationSpecToMetadataName as _locationSpecToMetadataName,
locationSpecToLocationEntity as _locationSpecToLocationEntity,
processingResult as _processingResult,
type EntitiesSearchFilter as _EntitiesSearchFilter,
type EntityFilter as _EntityFilter,
type DeferredEntity as _DeferredEntity,
type EntityRelationSpec as _EntityRelationSpec,
type CatalogProcessor as _CatalogProcessor,
type CatalogProcessorParser as _CatalogProcessorParser,
type CatalogProcessorCache as _CatalogProcessorCache,
type CatalogProcessorEmit as _CatalogProcessorEmit,
type CatalogProcessorLocationResult as _CatalogProcessorLocationResult,
type CatalogProcessorEntityResult as _CatalogProcessorEntityResult,
type CatalogProcessorRelationResult as _CatalogProcessorRelationResult,
type CatalogProcessorErrorResult as _CatalogProcessorErrorResult,
type CatalogProcessorRefreshKeysResult as _CatalogProcessorRefreshKeysResult,
type CatalogProcessorResult as _CatalogProcessorResult,
type EntityProvider as _EntityProvider,
type EntityProviderConnection as _EntityProviderConnection,
type EntityProviderMutation as _EntityProviderMutation,
type AnalyzeOptions as _AnalyzeOptions,
type PlaceholderResolver as _PlaceholderResolver,
type PlaceholderResolverParams as _PlaceholderResolverParams,
type PlaceholderResolverRead as _PlaceholderResolverRead,
type PlaceholderResolverResolveUrl as _PlaceholderResolverResolveUrl,
type LocationAnalyzer as _LocationAnalyzer,
type ScmLocationAnalyzer as _ScmLocationAnalyzer,
} from '@backstage/plugin-catalog-node';
import { Permission } from '@backstage/plugin-permission-common';
import {
defaultCatalogCollatorEntityTransformer as _defaultCatalogCollatorEntityTransformer,
type CatalogCollatorEntityTransformer as _CatalogCollatorEntityTransformer,
} from '@backstage/plugin-search-backend-module-catalog';
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
import { Readable } from 'stream';
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export const locationSpecToMetadataName = _locationSpecToMetadataName;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export const locationSpecToLocationEntity = _locationSpecToLocationEntity;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export const processingResult = _processingResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntitiesSearchFilter = _EntitiesSearchFilter;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntityFilter = _EntityFilter;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type DeferredEntity = _DeferredEntity;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntityRelationSpec = _EntityRelationSpec;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessor = _CatalogProcessor;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorParser = _CatalogProcessorParser;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorCache = _CatalogProcessorCache;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorEmit = _CatalogProcessorEmit;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorLocationResult = _CatalogProcessorLocationResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorEntityResult = _CatalogProcessorEntityResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorRelationResult = _CatalogProcessorRelationResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorErrorResult = _CatalogProcessorErrorResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorRefreshKeysResult =
_CatalogProcessorRefreshKeysResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type CatalogProcessorResult = _CatalogProcessorResult;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntityProvider = _EntityProvider;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntityProviderConnection = _EntityProviderConnection;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type EntityProviderMutation = _EntityProviderMutation;
/**
* Holds the entity location information.
*
* @remarks
*
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
* This flag is then set to indicate that the file can be not present.
* default value: 'required'.
*
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type LocationSpec = _LocationSpec;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type AnalyzeOptions = _AnalyzeOptions;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type LocationAnalyzer = _LocationAnalyzer;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type ScmLocationAnalyzer = _ScmLocationAnalyzer;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type PlaceholderResolver = _PlaceholderResolver;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type PlaceholderResolverParams = _PlaceholderResolverParams;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type PlaceholderResolverRead = _PlaceholderResolverRead;
/**
* @public
* @deprecated import from `@backstage/plugin-catalog-node` instead
*/
export type PlaceholderResolverResolveUrl = _PlaceholderResolverResolveUrl;
/**
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type AnalyzeLocationRequest = _AnalyzeLocationRequest;
/**
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type AnalyzeLocationResponse = _AnalyzeLocationResponse;
/**
* If the folder pointed to already contained catalog info yaml files, they are
* read and emitted like this so that the frontend can inform the user that it
* located them and can make sure to register them as well if they weren't
* already
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type AnalyzeLocationExistingEntity = _AnalyzeLocationExistingEntity;
/**
* This is some form of representation of what the analyzer could deduce.
* We should probably have a chat about how this can best be conveyed to
* the frontend. It'll probably contain a (possibly incomplete) entity, plus
* enough info for the frontend to know what form data to show to the user
* for overriding/completing the info.
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type AnalyzeLocationGenerateEntity = _AnalyzeLocationGenerateEntity;
/**
*
* This is where I get really vague. Something like this perhaps? Or it could be
* something like a json-schema that contains enough info for the frontend to
* be able to present a form and explanations
* @public
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
*/
export type AnalyzeLocationEntityField = _AnalyzeLocationEntityField;
namespace search {
const configKey = 'search.collators.catalog';
const defaults = {
schedule: {
frequency: { minutes: 10 },
timeout: { minutes: 15 },
initialDelay: { seconds: 3 },
},
collatorOptions: {
locationTemplate: '/catalog/:namespace/:kind/:name',
filter: undefined,
batchSize: 500,
},
};
export const readCollatorConfigOptions = (
configRoot: Config,
): {
locationTemplate: string;
filter: EntityFilterQuery | undefined;
batchSize: number;
} => {
const config = configRoot.getOptionalConfig(configKey);
if (!config) {
return defaults.collatorOptions;
}
return {
locationTemplate:
config.getOptionalString('locationTemplate') ??
defaults.collatorOptions.locationTemplate,
filter:
config.getOptional<EntityFilterQuery>('filter') ??
defaults.collatorOptions.filter,
batchSize:
config.getOptionalNumber('batchSize') ??
defaults.collatorOptions.batchSize,
};
};
export const getDocumentText = (entity: Entity): string => {
const documentTexts: string[] = [];
if (entity.metadata.description) {
documentTexts.push(entity.metadata.description);
}
if (isUserEntity(entity) || isGroupEntity(entity)) {
if (entity.spec?.profile?.displayName) {
documentTexts.push(entity.spec.profile.displayName);
}
}
if (isUserEntity(entity)) {
if (entity.spec?.profile?.email) {
documentTexts.push(entity.spec.profile.email);
}
}
return documentTexts.join(' : ');
};
}
/**
* @public
* @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead
*/
export const defaultCatalogCollatorEntityTransformer =
_defaultCatalogCollatorEntityTransformer;
/**
* @public
* @deprecated This is no longer supported since the new backend system migration
*/
export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory {
public readonly type = 'software-catalog';
public readonly visibilityPermission: Permission =
catalogEntityReadPermission;
private locationTemplate: string;
private filter?: GetEntitiesRequest['filter'];
private batchSize: number;
private readonly catalogClient: CatalogApi;
private entityTransformer: CatalogCollatorEntityTransformer;
private auth: AuthService;
static fromConfig(
configRoot: Config,
options: DefaultCatalogCollatorFactoryOptions,
) {
const configOptions = search.readCollatorConfigOptions(configRoot);
const { auth: adaptedAuth } = createLegacyAuthAdapters({
auth: options.auth,
discovery: options.discovery,
tokenManager: options.tokenManager,
});
return new DefaultCatalogCollatorFactory({
locationTemplate:
options.locationTemplate ?? configOptions.locationTemplate,
filter: options.filter ?? configOptions.filter,
batchSize: options.batchSize ?? configOptions.batchSize,
entityTransformer: options.entityTransformer,
auth: adaptedAuth,
discovery: options.discovery,
catalogClient: options.catalogClient,
});
}
private constructor(options: {
locationTemplate: string;
filter: GetEntitiesRequest['filter'];
batchSize: number;
entityTransformer?: CatalogCollatorEntityTransformer;
auth: AuthService;
discovery: DiscoveryService;
catalogClient?: CatalogApi;
}) {
const {
auth,
batchSize,
discovery,
locationTemplate,
filter,
catalogClient,
entityTransformer,
} = options;
this.locationTemplate = locationTemplate;
this.filter = filter;
this.batchSize = batchSize;
this.catalogClient =
catalogClient || new CatalogClient({ discoveryApi: discovery });
this.entityTransformer =
entityTransformer ?? defaultCatalogCollatorEntityTransformer;
this.auth = auth;
}
async getCollator(): Promise<Readable> {
return Readable.from(this.execute());
}
private async *execute(): AsyncGenerator<CatalogEntityDocument> {
let entitiesRetrieved = 0;
let cursor: string | undefined = undefined;
do {
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const response = await this.catalogClient.queryEntities(
{
filter: this.filter,
limit: this.batchSize,
...(cursor ? { cursor } : {}),
},
{ token },
);
cursor = response.pageInfo.nextCursor;
entitiesRetrieved += response.items.length;
for (const entity of response.items) {
yield {
...this.entityTransformer(entity),
authorization: {
resourceRef: stringifyEntityRef(entity),
},
location: this.applyArgsToFormat(this.locationTemplate, {
namespace: entity.metadata.namespace || 'default',
kind: entity.kind,
name: entity.metadata.name,
}),
};
}
} while (cursor);
}
private applyArgsToFormat(
format: string,
args: Record<string, string>,
): string {
let formatted = format;
for (const [key, value] of Object.entries(args)) {
formatted = formatted.replace(`:${key}`, value);
}
return formatted.toLowerCase();
}
}
/**
* @public
* @deprecated This is no longer supported since the new backend system migration
*/
export type DefaultCatalogCollatorFactoryOptions = {
auth?: AuthService;
discovery: DiscoveryService;
tokenManager?: TokenManager;
/**
* @deprecated Use the config key `search.collators.catalog.locationTemplate` instead.
*/
locationTemplate?: string;
/**
* @deprecated Use the config key `search.collators.catalog.filter` instead.
*/
filter?: GetEntitiesRequest['filter'];
/**
* @deprecated Use the config key `search.collators.catalog.batchSize` instead.
*/
batchSize?: number;
// TODO(freben): Change to required CatalogService instead when fully migrated to the new backend system.
catalogClient?: CatalogApi;
/**
* Allows you to customize how entities are shaped into documents.
*/
entityTransformer?: CatalogCollatorEntityTransformer;
};
/**
* @public
* @deprecated import from `@backstage/plugin-search-backend-module-catalog` instead
*/
export type CatalogCollatorEntityTransformer =
_CatalogCollatorEntityTransformer;
-2
View File
@@ -23,8 +23,6 @@
export { catalogPlugin as default } from './service/CatalogPlugin';
export * from './processors';
export * from './processing';
export * from './search';
export * from './service';
export * from './deprecated';
export * from './constants';
export * from './util';
@@ -1,87 +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.
*/
import { ConfigReader } from '@backstage/config';
import {
ScmIntegrations,
ScmIntegrationRegistry,
} from '@backstage/integration';
import path from 'path';
import { toAbsoluteUrl } from './LocationEntityProcessor';
import { LocationSpec } from '@backstage/plugin-catalog-common';
describe('LocationEntityProcessor', () => {
describe('toAbsoluteUrl', () => {
it('handles files', () => {
const integrations = {} as unknown as ScmIntegrationRegistry;
const base: LocationSpec = {
type: 'file',
target: `some${path.sep}path${path.sep}catalog-info.yaml`,
};
expect(toAbsoluteUrl(integrations, base, `.${path.sep}c`)).toBe(
`some${path.sep}path${path.sep}c`,
);
expect(toAbsoluteUrl(integrations, base, `${path.sep}c`)).toBe(
`${path.sep}c`,
);
});
it('handles urls', () => {
const integrations = ScmIntegrations.fromConfig(new ConfigReader({}));
const base: LocationSpec = {
type: 'url',
target: 'http://a.com/b/catalog-info.yaml',
};
jest.spyOn(integrations, 'resolveUrl');
expect(toAbsoluteUrl(integrations, base, './c/d')).toBe(
'http://a.com/b/c/d',
);
expect(toAbsoluteUrl(integrations, base, 'c/d')).toBe(
'http://a.com/b/c/d',
);
expect(toAbsoluteUrl(integrations, base, 'http://b.com/z')).toBe(
'http://b.com/z',
);
expect(integrations.resolveUrl).toHaveBeenCalledTimes(3);
});
it('handles azure urls specifically', () => {
const integrations = ScmIntegrations.fromConfig(
new ConfigReader({
integrations: {
azure: [{ host: 'dev.azure.com' }],
},
}),
);
expect(
toAbsoluteUrl(
integrations,
{
type: 'url',
target:
'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml',
},
'./a.yaml',
),
).toBe(
'https://dev.azure.com/organization/project/_git/repository?path=%2Fa.yaml',
);
});
});
});
@@ -1,110 +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.
*/
import { Entity, LocationEntity } from '@backstage/catalog-model';
import { ScmIntegrationRegistry } from '@backstage/integration';
import path from 'path';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import {
processingResult,
CatalogProcessor,
CatalogProcessorEmit,
} from '@backstage/plugin-catalog-node';
export function toAbsoluteUrl(
integrations: ScmIntegrationRegistry,
base: LocationSpec,
target: string,
): string {
try {
if (base.type === 'file') {
if (target.startsWith('.')) {
return path.join(path.dirname(base.target), target);
}
return target;
}
return integrations.resolveUrl({ url: target, base: base.target });
} catch (e) {
return target;
}
}
/**
* @public
* @deprecated This processor should no longer be used
*/
export type LocationEntityProcessorOptions = {
integrations: ScmIntegrationRegistry;
};
/**
* Legacy processor, should not be used.
*
* @remarks
*
* In the old catalog architecture, this processor translated Location entities
* into URLs that should be fetched. This is no longer needed since the engine
* handles this internally.
*
* @public
* @deprecated This processor should no longer be used
*/
export class LocationEntityProcessor implements CatalogProcessor {
constructor(private readonly options: LocationEntityProcessorOptions) {}
getProcessorName(): string {
return 'LocationEntityProcessor';
}
async postProcessEntity(
entity: Entity,
location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity> {
if (entity.kind === 'Location') {
const locationEntity = entity as LocationEntity;
const type = locationEntity.spec.type || location.type;
if (type === 'file' && location.target.endsWith(path.sep)) {
emit(
processingResult.inputError(
location,
`LocationEntityProcessor cannot handle ${type} type location with target ${location.target} that ends with a path separator`,
),
);
}
const targets = new Array<string>();
if (locationEntity.spec.target) {
targets.push(locationEntity.spec.target);
}
if (locationEntity.spec.targets) {
targets.push(...locationEntity.spec.targets);
}
for (const maybeRelativeTarget of targets) {
const target = toAbsoluteUrl(
this.options.integrations,
location,
maybeRelativeTarget,
);
emit(processingResult.location({ type, target }));
}
}
return entity;
}
}
@@ -19,8 +19,6 @@ export { AnnotateScmSlugEntityProcessor } from './AnnotateScmSlugEntityProcessor
export { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor';
export { CodeOwnersProcessor } from './CodeOwnersProcessor';
export { FileReaderProcessor } from './FileReaderProcessor';
export { LocationEntityProcessor } from './LocationEntityProcessor';
export type { LocationEntityProcessorOptions } from './LocationEntityProcessor';
export { PlaceholderProcessor } from './PlaceholderProcessor';
export type { PlaceholderProcessorOptions } from './PlaceholderProcessor';
export { UrlReaderProcessor } from './UrlReaderProcessor';
@@ -1,159 +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 { TokenManager } from '@backstage/backend-common';
import {
mockServices,
registerMswTestHooks,
} from '@backstage/backend-test-utils';
import { Entity } from '@backstage/catalog-model';
import { DefaultCatalogCollator } from './DefaultCatalogCollator';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { ConfigReader } from '@backstage/config';
const server = setupServer();
const expectedEntities: Entity[] = [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'test-entity',
description: 'The expected description',
},
spec: {
type: 'some-type',
lifecycle: 'experimental',
owner: 'someone',
},
},
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
title: 'Test Entity',
name: 'test-entity-2',
description: 'The expected description 2',
},
spec: {
type: 'some-type',
lifecycle: 'experimental',
owner: 'someone',
},
},
];
describe('DefaultCatalogCollator', () => {
const mockDiscovery = mockServices.discovery.mock({
getBaseUrl: async () => 'http://localhost:7007',
});
let mockTokenManager: jest.Mocked<TokenManager>;
let collator: DefaultCatalogCollator;
registerMswTestHooks(server);
beforeAll(() => {
mockTokenManager = {
getToken: jest.fn().mockResolvedValue({ token: '' }),
authenticate: jest.fn(),
};
collator = new DefaultCatalogCollator({
discovery: mockDiscovery,
tokenManager: mockTokenManager,
});
});
beforeEach(() => {
server.use(
rest.get('http://localhost:7007/entities', (req, res, ctx) => {
if (req.url.searchParams.has('filter')) {
const filter = req.url.searchParams.get('filter');
if (filter === 'kind=Foo,kind=Bar') {
// When filtering on the 'Foo,Bar' kinds we simply return no items, to simulate a filter
return res(ctx.json([]));
}
throw new Error('Unexpected filter parameter');
}
return res(ctx.json(expectedEntities));
}),
);
});
afterAll(() => {
jest.useRealTimers();
});
it('fetches from the configured catalog service', async () => {
const documents = await collator.execute();
expect(mockDiscovery.getBaseUrl).toHaveBeenCalledWith('catalog');
expect(documents).toHaveLength(expectedEntities.length);
});
it('maps a returned entity to an expected CatalogEntityDocument', async () => {
const documents = await collator.execute();
expect(documents[0]).toMatchObject({
title: expectedEntities[0].metadata.name,
location: '/catalog/default/component/test-entity',
text: expectedEntities[0].metadata.description,
namespace: 'default',
componentType: expectedEntities[0]!.spec!.type,
lifecycle: expectedEntities[0]!.spec!.lifecycle,
owner: expectedEntities[0]!.spec!.owner,
authorization: {
resourceRef: 'component:default/test-entity',
},
});
expect(documents[1]).toMatchObject({
title: `${expectedEntities[1].metadata.title} (${expectedEntities[1].metadata.name})`,
location: '/catalog/default/component/test-entity-2',
text: expectedEntities[1].metadata.description,
namespace: 'default',
componentType: expectedEntities[1]!.spec!.type,
lifecycle: expectedEntities[1]!.spec!.lifecycle,
owner: expectedEntities[1]!.spec!.owner,
authorization: {
resourceRef: 'component:default/test-entity-2',
},
});
});
it('maps a returned entity with a custom locationTemplate', async () => {
// Provide an alternate location template.
collator = new DefaultCatalogCollator({
discovery: mockDiscovery,
tokenManager: mockTokenManager,
locationTemplate: '/software/:name',
});
const documents = await collator.execute();
expect(documents[0]).toMatchObject({
location: '/software/test-entity',
});
});
it('allows filtering of the retrieved catalog entities', async () => {
// Provide an alternate location template.
collator = DefaultCatalogCollator.fromConfig(new ConfigReader({}), {
discovery: mockDiscovery,
tokenManager: mockTokenManager,
filter: {
kind: ['Foo', 'Bar'],
},
});
const documents = await collator.execute();
// The simulated 'Foo,Bar' filter should return in an empty list
expect(documents).toHaveLength(0);
});
});
@@ -1,137 +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 { TokenManager } from '@backstage/backend-common';
import {
Entity,
isUserEntity,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import {
CatalogApi,
CatalogClient,
GetEntitiesRequest,
} from '@backstage/catalog-client';
import { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha';
import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
import { Permission } from '@backstage/plugin-permission-common';
import { DiscoveryService } from '@backstage/backend-plugin-api';
/**
* @public
* @deprecated Upgrade to a more recent `@backstage/plugin-search-backend-node` and
* use `DefaultCatalogCollatorFactory` instead.
*/
export class DefaultCatalogCollator {
protected discovery: DiscoveryService;
protected locationTemplate: string;
protected filter?: GetEntitiesRequest['filter'];
protected readonly catalogClient: CatalogApi;
public readonly type: string = 'software-catalog';
public readonly visibilityPermission: Permission =
catalogEntityReadPermission;
protected tokenManager: TokenManager;
static fromConfig(
_config: Config,
options: {
discovery: DiscoveryService;
tokenManager: TokenManager;
filter?: GetEntitiesRequest['filter'];
},
) {
return new DefaultCatalogCollator({
...options,
});
}
constructor(options: {
discovery: DiscoveryService;
tokenManager: TokenManager;
locationTemplate?: string;
filter?: GetEntitiesRequest['filter'];
catalogClient?: CatalogApi;
}) {
const { discovery, locationTemplate, filter, catalogClient, tokenManager } =
options;
this.discovery = discovery;
this.locationTemplate =
locationTemplate || '/catalog/:namespace/:kind/:name';
this.filter = filter;
this.catalogClient =
catalogClient || new CatalogClient({ discoveryApi: discovery });
this.tokenManager = tokenManager;
}
protected applyArgsToFormat(
format: string,
args: Record<string, string>,
): string {
let formatted = format;
for (const [key, value] of Object.entries(args)) {
formatted = formatted.replace(`:${key}`, value);
}
return formatted.toLowerCase();
}
private getDocumentText(entity: Entity): string {
let documentText = entity.metadata.description || '';
if (isUserEntity(entity)) {
if (entity.spec?.profile?.displayName && documentText) {
// combine displayName and description
const displayName = entity.spec?.profile?.displayName;
documentText = displayName.concat(' : ', documentText);
} else {
documentText = entity.spec?.profile?.displayName || documentText;
}
}
return documentText;
}
async execute() {
const { token } = await this.tokenManager.getToken();
const response = await this.catalogClient.getEntities(
{
filter: this.filter,
},
{ token },
);
return response.items.map((entity: Entity): CatalogEntityDocument => {
return {
title: entity.metadata.title
? `${entity.metadata.title} (${entity.metadata.name})`
: entity.metadata.name,
location: this.applyArgsToFormat(this.locationTemplate, {
namespace: entity.metadata.namespace || 'default',
kind: entity.kind,
name: entity.metadata.name,
}),
text: this.getDocumentText(entity),
componentType: entity.spec?.type?.toString() || 'other',
type: entity.spec?.type?.toString() || 'other',
namespace: entity.metadata.namespace || 'default',
kind: entity.kind,
lifecycle: (entity.spec?.lifecycle as string) || '',
owner: (entity.spec?.owner as string) || '',
authorization: {
resourceRef: stringifyEntityRef(entity),
},
};
});
}
}
@@ -1,20 +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.
*/
/**
* todo(backstage/techdocs-core): stop exporting this in a future release.
*/
export { DefaultCatalogCollator } from './DefaultCatalogCollator';
-2
View File
@@ -6032,8 +6032,6 @@ __metadata:
"@backstage/plugin-events-node": "workspace:^"
"@backstage/plugin-permission-common": "workspace:^"
"@backstage/plugin-permission-node": "workspace:^"
"@backstage/plugin-search-backend-module-catalog": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@backstage/repo-tools": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/types": "workspace:^"