restore the search collator types to not break the api
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Removed the long-deprecated `DefaultCatalogCollatorFactory` and `DefaultCatalogCollatorFactoryOptions` exports, which now no longer exist in the search plugin's offerings. If you were using these, you want to migrate to [the new backend system](https://backstage.io/docs/backend-system/) and use the [catalog collator](https://backstage.io/docs/features/search/collators#catalog) directly.
|
||||
Internalize the deprecated collator types since they were removed from the collator itself during new-backend-system migration.
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
"@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",
|
||||
|
||||
@@ -30,6 +30,7 @@ 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 { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter as EntityFilter_2 } from '@backstage/plugin-catalog-node';
|
||||
@@ -56,6 +57,7 @@ import { PlaceholderResolver as PlaceholderResolver_2 } from '@backstage/plugin-
|
||||
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 { RootConfigService } from '@backstage/backend-plugin-api';
|
||||
import { Router } from 'express';
|
||||
import { SchedulerService } from '@backstage/backend-plugin-api';
|
||||
@@ -323,6 +325,33 @@ export class DefaultCatalogCollator {
|
||||
// @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;
|
||||
|
||||
|
||||
@@ -15,6 +15,25 @@
|
||||
*/
|
||||
|
||||
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,
|
||||
@@ -22,6 +41,7 @@ import {
|
||||
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,
|
||||
@@ -51,10 +71,13 @@ import {
|
||||
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
|
||||
@@ -252,6 +275,69 @@ export type AnalyzeLocationGenerateEntity = _AnalyzeLocationGenerateEntity;
|
||||
*/
|
||||
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
|
||||
@@ -259,6 +345,155 @@ export type AnalyzeLocationEntityField = _AnalyzeLocationEntityField;
|
||||
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
|
||||
|
||||
@@ -6194,6 +6194,7 @@ __metadata:
|
||||
"@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/types": "workspace:^"
|
||||
"@opentelemetry/api": ^1.9.0
|
||||
|
||||
Reference in New Issue
Block a user