Merge pull request #29740 from backstage/freben/catalog2
catalog-backend: remove all deprecated exports, remove old backend system support
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING ALPHA**: You can no longer import the catalog plugin from the `/alpha` export; please use the regular root default export instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-node': minor
|
||||
---
|
||||
|
||||
Added `parseEntityYaml` from `@backstage/plugin-catalog-backend`, to make it more easily usable by custom plugins and modules
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': major
|
||||
---
|
||||
|
||||
**BREAKING**: Removed all deprecated exports, and removed support for the old backend system.
|
||||
|
||||
It also removes the `CodeOwnersProcessor` from the default set of processors, because it is expensive to run and has vague semantics. You need to update your backend to add it to the `catalogProcessingExtensionPoint` if you wish to continue using it.
|
||||
|
||||
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`
|
||||
- `parseEntityYaml`
|
||||
|
||||
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`
|
||||
- `CatalogBuilder`
|
||||
- `CatalogEnvironment`
|
||||
- `CatalogPermissionRuleInput`
|
||||
- `CatalogProcessingEngine`
|
||||
- `createRandomProcessingInterval`
|
||||
- `ProcessingIntervalFunction`
|
||||
@@ -17,5 +17,5 @@
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
|
||||
const backend = createBackend();
|
||||
backend.add(import('../src/alpha'));
|
||||
backend.add(import('../src'));
|
||||
backend.start();
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.25.0",
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-client": "workspace:^",
|
||||
@@ -74,11 +73,8 @@
|
||||
"@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",
|
||||
"codeowners-utils": "^1.0.2",
|
||||
"core-js": "^3.6.5",
|
||||
"express": "^4.17.1",
|
||||
@@ -105,6 +101,7 @@
|
||||
"@backstage/repo-tools": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@types/core-js": "^2.5.4",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common';
|
||||
import { Conditions } from '@backstage/plugin-permission-node';
|
||||
import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node';
|
||||
@@ -87,10 +86,6 @@ export const createCatalogPermissionRule: <
|
||||
rule: PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>,
|
||||
) => PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
const _feature: BackendFeature;
|
||||
export default _feature;
|
||||
|
||||
// @alpha
|
||||
export const permissionRules: {
|
||||
hasAnnotation: PermissionRule<
|
||||
|
||||
@@ -3,104 +3,36 @@
|
||||
> 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 { 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';
|
||||
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 { 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 { 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';
|
||||
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 { RootConfigService } from '@backstage/backend-plugin-api';
|
||||
import { Router } from 'express';
|
||||
import { SchedulerService } from '@backstage/backend-plugin-api';
|
||||
import { PlaceholderResolver } from '@backstage/plugin-catalog-node';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { ScmLocationAnalyzer as ScmLocationAnalyzer_2 } from '@backstage/plugin-catalog-node';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
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 +47,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>;
|
||||
@@ -138,126 +70,12 @@ export const CATALOG_CONFLICTS_TOPIC = 'experimental.catalog.conflict';
|
||||
// @public (undocumented)
|
||||
export const CATALOG_ERRORS_TOPIC = 'experimental.catalog.errors';
|
||||
|
||||
// @public @deprecated
|
||||
export class CatalogBuilder {
|
||||
addEntityPolicy(
|
||||
...policies: Array<EntityPolicy | Array<EntityPolicy>>
|
||||
): CatalogBuilder;
|
||||
addEntityProvider(
|
||||
...providers: Array<EntityProvider_2 | Array<EntityProvider_2>>
|
||||
): CatalogBuilder;
|
||||
addLocationAnalyzers(
|
||||
...analyzers: Array<ScmLocationAnalyzer_2 | Array<ScmLocationAnalyzer_2>>
|
||||
): CatalogBuilder;
|
||||
addPermissionRules(
|
||||
...permissionRules: Array<
|
||||
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
|
||||
>
|
||||
): this;
|
||||
addPermissions(...permissions: Array<Permission | Array<Permission>>): this;
|
||||
addProcessor(
|
||||
...processors: Array<CatalogProcessor_2 | Array<CatalogProcessor_2>>
|
||||
): CatalogBuilder;
|
||||
build(): Promise<{
|
||||
processingEngine: CatalogProcessingEngine;
|
||||
router: Router;
|
||||
}>;
|
||||
static create(env: CatalogEnvironment): CatalogBuilder;
|
||||
getDefaultProcessors(): CatalogProcessor_2[];
|
||||
replaceEntityPolicies(policies: EntityPolicy[]): CatalogBuilder;
|
||||
replaceProcessors(processors: CatalogProcessor_2[]): CatalogBuilder;
|
||||
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
|
||||
setEntityDataParser(parser: CatalogProcessorParser_2): CatalogBuilder;
|
||||
setEventBroker(broker: EventBroker | EventsService): CatalogBuilder;
|
||||
setFieldFormatValidators(validators: Partial<Validators>): CatalogBuilder;
|
||||
setLocationAnalyzer(locationAnalyzer: LocationAnalyzer_2): CatalogBuilder;
|
||||
setPlaceholderResolver(
|
||||
key: string,
|
||||
resolver: PlaceholderResolver_2,
|
||||
): CatalogBuilder;
|
||||
setProcessingInterval(
|
||||
processingInterval: ProcessingIntervalFunction,
|
||||
): CatalogBuilder;
|
||||
setProcessingIntervalSeconds(seconds: number): CatalogBuilder;
|
||||
// (undocumented)
|
||||
subscribe(options: {
|
||||
onProcessingError: (event: {
|
||||
unprocessedEntity: Entity;
|
||||
errors: Error[];
|
||||
}) => Promise<void> | void;
|
||||
}): void;
|
||||
useLegacySingleProcessorValidation(): this;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type CatalogCollatorEntityTransformer =
|
||||
CatalogCollatorEntityTransformer_2;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type CatalogEnvironment = {
|
||||
logger: LoggerService;
|
||||
database: DatabaseService;
|
||||
config: RootConfigService;
|
||||
reader: UrlReaderService;
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
permissionsRegistry?: PermissionsRegistryService;
|
||||
scheduler?: SchedulerService;
|
||||
discovery?: DiscoveryService;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
auditor?: AuditorService;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type CatalogPermissionRuleInput<
|
||||
TParams extends PermissionRuleParams = PermissionRuleParams,
|
||||
> = PermissionRule<Entity, EntitiesSearchFilter_2, 'catalog-entity', TParams>;
|
||||
|
||||
// @public
|
||||
const catalogPlugin: BackendFeature;
|
||||
export default catalogPlugin;
|
||||
|
||||
// @public
|
||||
export interface CatalogProcessingEngine {
|
||||
// (undocumented)
|
||||
start(): Promise<void>;
|
||||
// (undocumented)
|
||||
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,227 +92,49 @@ 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
|
||||
export function createRandomProcessingInterval(options: {
|
||||
minSeconds: number;
|
||||
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>;
|
||||
|
||||
// @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 +144,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>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -14,10 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { catalogPlugin } from './service/CatalogPlugin';
|
||||
|
||||
/** @alpha */
|
||||
const _feature = catalogPlugin;
|
||||
export default _feature;
|
||||
|
||||
export * from './permissions';
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
DbRefreshStateRow,
|
||||
DbRelationsRow,
|
||||
} from './tables';
|
||||
import { createRandomProcessingInterval } from '../processing';
|
||||
import { createRandomProcessingInterval } from '../processing/refresh';
|
||||
import { timestampToDateTime } from './conversion';
|
||||
import { generateStableHash } from './util';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ConflictError } from '@backstage/errors';
|
||||
import { DeferredEntity } from '@backstage/plugin-catalog-node';
|
||||
import { Knex } from 'knex';
|
||||
import lodash from 'lodash';
|
||||
import { ProcessingIntervalFunction } from '../processing';
|
||||
import { ProcessingIntervalFunction } from '../processing/refresh';
|
||||
import { rethrowError, timestampToDateTime } from './conversion';
|
||||
import { initDatabaseMetrics } from './metrics';
|
||||
import {
|
||||
|
||||
@@ -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;
|
||||
@@ -22,9 +22,4 @@
|
||||
|
||||
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';
|
||||
|
||||
@@ -84,7 +84,6 @@ function addProcessorAttributes(
|
||||
);
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export class DefaultCatalogProcessingOrchestrator
|
||||
implements CatalogProcessingOrchestrator
|
||||
{
|
||||
|
||||
@@ -1,19 +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 type { CatalogProcessingEngine } from './types';
|
||||
export { createRandomProcessingInterval } from './refresh';
|
||||
export type { ProcessingIntervalFunction } from './refresh';
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
/**
|
||||
* Function that returns the catalog processing interval in seconds.
|
||||
* @public
|
||||
*/
|
||||
export type ProcessingIntervalFunction = () => number;
|
||||
|
||||
/**
|
||||
* Creates a function that returns a random processing interval between minSeconds and maxSeconds.
|
||||
* @returns A {@link ProcessingIntervalFunction} that provides the next processing interval
|
||||
* @public
|
||||
*/
|
||||
export function createRandomProcessingInterval(options: {
|
||||
minSeconds: number;
|
||||
|
||||
@@ -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';
|
||||
@@ -14,10 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
createLegacyAuthAdapters,
|
||||
HostDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import {
|
||||
DefaultNamespaceEntityPolicy,
|
||||
Entity,
|
||||
@@ -38,7 +34,6 @@ import {
|
||||
AuditorService,
|
||||
AuthService,
|
||||
DatabaseService,
|
||||
DiscoveryService,
|
||||
HttpAuthService,
|
||||
LoggerService,
|
||||
PermissionsRegistryService,
|
||||
@@ -55,7 +50,6 @@ import {
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorParser,
|
||||
EntitiesSearchFilter,
|
||||
EntityProvider,
|
||||
LocationAnalyzer,
|
||||
PlaceholderResolver,
|
||||
@@ -65,13 +59,11 @@ import { EventBroker, EventsService } from '@backstage/plugin-events-node';
|
||||
import {
|
||||
Permission,
|
||||
PermissionAuthorizer,
|
||||
PermissionRuleParams,
|
||||
toPermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
createConditionTransformer,
|
||||
createPermissionIntegrationRouter,
|
||||
PermissionRule,
|
||||
} from '@backstage/plugin-permission-node';
|
||||
import { durationToMilliseconds } from '@backstage/types';
|
||||
import { DefaultCatalogDatabase } from '../database/DefaultCatalogDatabase';
|
||||
@@ -81,11 +73,11 @@ import { applyDatabaseMigrations } from '../database/migrations';
|
||||
import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules';
|
||||
import { RepoLocationAnalyzer } from '../ingestion/LocationAnalyzer';
|
||||
import { permissionRules as catalogPermissionRules } from '../permissions/rules';
|
||||
import { CatalogProcessingEngine } from '../processing/types';
|
||||
import {
|
||||
CatalogProcessingEngine,
|
||||
createRandomProcessingInterval,
|
||||
ProcessingIntervalFunction,
|
||||
} from '../processing';
|
||||
} from '../processing/refresh';
|
||||
import { connectEntityProviders } from '../processing/connectEntityProviders';
|
||||
import { evictEntitiesFromOrphanedProviders } from '../processing/evictEntitiesFromOrphanedProviders';
|
||||
import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine';
|
||||
@@ -93,7 +85,6 @@ import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatal
|
||||
import {
|
||||
AnnotateLocationEntityProcessor,
|
||||
BuiltinKindsEntityProcessor,
|
||||
CodeOwnersProcessor,
|
||||
FileReaderProcessor,
|
||||
PlaceholderProcessor,
|
||||
UrlReaderProcessor,
|
||||
@@ -116,21 +107,11 @@ import { DefaultEntitiesCatalog } from './DefaultEntitiesCatalog';
|
||||
import { DefaultLocationService } from './DefaultLocationService';
|
||||
import { DefaultRefreshService } from './DefaultRefreshService';
|
||||
import { entitiesResponseToObjects } from './response';
|
||||
import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha';
|
||||
import {
|
||||
catalogEntityPermissionResourceRef,
|
||||
CatalogPermissionRuleInput,
|
||||
} from '@backstage/plugin-catalog-node/alpha';
|
||||
|
||||
/**
|
||||
* This is a duplicate of the alpha `CatalogPermissionRule` type, for use in the stable API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type CatalogPermissionRuleInput<
|
||||
TParams extends PermissionRuleParams = PermissionRuleParams,
|
||||
> = PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
|
||||
|
||||
/**
|
||||
* @deprecated Please migrate to the new backend system as this will be removed in the future.
|
||||
* @public
|
||||
*/
|
||||
export type CatalogEnvironment = {
|
||||
logger: LoggerService;
|
||||
database: DatabaseService;
|
||||
@@ -139,9 +120,8 @@ export type CatalogEnvironment = {
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
permissionsRegistry?: PermissionsRegistryService;
|
||||
scheduler?: SchedulerService;
|
||||
discovery?: DiscoveryService;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
auditor?: AuditorService;
|
||||
};
|
||||
|
||||
@@ -167,9 +147,6 @@ export type CatalogEnvironment = {
|
||||
* - Processors can be added or replaced. These implement the functionality of
|
||||
* reading, parsing, validating, and processing the entity data before it is
|
||||
* persisted in the catalog.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Please migrate to the new backend system as this will be removed in the future.
|
||||
*/
|
||||
export class CatalogBuilder {
|
||||
private readonly env: CatalogEnvironment;
|
||||
@@ -380,7 +357,6 @@ export class CatalogBuilder {
|
||||
return [
|
||||
new FileReaderProcessor(),
|
||||
new UrlReaderProcessor({ reader, logger, config }),
|
||||
CodeOwnersProcessor.fromConfig(config, { logger, reader }),
|
||||
new AnnotateLocationEntityProcessor({ integrations }),
|
||||
];
|
||||
}
|
||||
@@ -482,15 +458,11 @@ export class CatalogBuilder {
|
||||
permissions,
|
||||
scheduler,
|
||||
permissionsRegistry,
|
||||
discovery = HostDiscovery.fromConfig(config),
|
||||
auditor,
|
||||
auth,
|
||||
httpAuth,
|
||||
} = this.env;
|
||||
|
||||
const { auth, httpAuth } = createLegacyAuthAdapters({
|
||||
...this.env,
|
||||
discovery,
|
||||
});
|
||||
|
||||
const disableRelationsCompatibility = config.getOptionalBoolean(
|
||||
'catalog.disableRelationsCompatibility',
|
||||
);
|
||||
|
||||
@@ -35,13 +35,14 @@ import {
|
||||
catalogModelExtensionPoint,
|
||||
CatalogPermissionExtensionPoint,
|
||||
catalogPermissionExtensionPoint,
|
||||
CatalogPermissionRuleInput,
|
||||
CatalogProcessingExtensionPoint,
|
||||
catalogProcessingExtensionPoint,
|
||||
} from '@backstage/plugin-catalog-node/alpha';
|
||||
import { eventsServiceRef } from '@backstage/plugin-events-node';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { merge } from 'lodash';
|
||||
import { CatalogBuilder, CatalogPermissionRuleInput } from './CatalogBuilder';
|
||||
import { CatalogBuilder } from './CatalogBuilder';
|
||||
|
||||
class CatalogLocationsExtensionPointImpl
|
||||
implements CatalogLocationsExtensionPoint
|
||||
@@ -232,7 +233,6 @@ export const catalogPlugin = createBackendPlugin({
|
||||
httpRouter: coreServices.httpRouter,
|
||||
lifecycle: coreServices.rootLifecycle,
|
||||
scheduler: coreServices.scheduler,
|
||||
discovery: coreServices.discovery,
|
||||
auth: coreServices.auth,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
auditor: coreServices.auditor,
|
||||
@@ -248,7 +248,6 @@ export const catalogPlugin = createBackendPlugin({
|
||||
httpRouter,
|
||||
lifecycle,
|
||||
scheduler,
|
||||
discovery,
|
||||
auth,
|
||||
httpAuth,
|
||||
auditor,
|
||||
@@ -262,7 +261,6 @@ export const catalogPlugin = createBackendPlugin({
|
||||
database,
|
||||
scheduler,
|
||||
logger,
|
||||
discovery,
|
||||
auth,
|
||||
httpAuth,
|
||||
auditor,
|
||||
|
||||
@@ -65,9 +65,6 @@ import {
|
||||
|
||||
/**
|
||||
* Options used by {@link createRouter}.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Please migrate to the new backend system as this will be removed in the future.
|
||||
*/
|
||||
export interface RouterOptions {
|
||||
entitiesCatalog?: EntitiesCatalog;
|
||||
|
||||
@@ -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 type {
|
||||
CatalogEnvironment,
|
||||
CatalogPermissionRuleInput,
|
||||
} from './CatalogBuilder';
|
||||
export { CatalogBuilder } from './CatalogBuilder';
|
||||
@@ -20,8 +20,6 @@ import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* Holds the information required to create a new location in the catalog location store.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface LocationInput {
|
||||
type: string;
|
||||
@@ -30,7 +28,6 @@ export interface LocationInput {
|
||||
|
||||
/**
|
||||
* The location service manages entity locations.
|
||||
* @public
|
||||
*/
|
||||
export interface LocationService {
|
||||
createLocation(
|
||||
@@ -59,8 +56,6 @@ export interface LocationService {
|
||||
|
||||
/**
|
||||
* Options for requesting a refresh of entities in the catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type RefreshOptions = {
|
||||
/** The reference to a single entity that should be refreshed */
|
||||
@@ -70,8 +65,6 @@ export type RefreshOptions = {
|
||||
|
||||
/**
|
||||
* A service that manages refreshes of entities in the catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RefreshService {
|
||||
/**
|
||||
@@ -82,7 +75,6 @@ export interface RefreshService {
|
||||
|
||||
/**
|
||||
* Interacts with the database to manage locations.
|
||||
* @public
|
||||
*/
|
||||
export interface LocationStore {
|
||||
createLocation(location: LocationInput): Promise<Location>;
|
||||
|
||||
@@ -47,14 +47,13 @@ import {
|
||||
} from '../processing/DefaultCatalogProcessingEngine';
|
||||
import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator';
|
||||
import { connectEntityProviders } from '../processing/connectEntityProviders';
|
||||
import { CatalogProcessingEngine } from '../processing';
|
||||
import { CatalogProcessingEngine } from '../processing/types';
|
||||
import { DefaultEntitiesCatalog } from '../service/DefaultEntitiesCatalog';
|
||||
import { DefaultRefreshService } from '../service/DefaultRefreshService';
|
||||
import { RefreshOptions, RefreshService } from '../service/types';
|
||||
import { DefaultStitcher } from '../stitching/DefaultStitcher';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { mockServices, TestDatabases } from '@backstage/backend-test-utils';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { DatabaseManager } from '@backstage/backend-common';
|
||||
import { entitiesResponseToObjects } from '../service/response';
|
||||
import { deleteOrphanedEntities } from '../database/operations/util/deleteOrphanedEntities';
|
||||
|
||||
@@ -205,10 +204,10 @@ class TestHarness {
|
||||
readonly #proxyProgressTracker: ProxyProgressTracker;
|
||||
readonly #db: Knex;
|
||||
|
||||
static async create(options?: {
|
||||
static async create(options: {
|
||||
disableRelationsCompatibility?: boolean;
|
||||
logger?: LoggerService;
|
||||
db?: Knex;
|
||||
db: Knex;
|
||||
permissions?: PermissionEvaluator;
|
||||
additionalProviders?: EntityProvider[];
|
||||
processEntity?(
|
||||
@@ -231,24 +230,19 @@ class TestHarness {
|
||||
},
|
||||
});
|
||||
const logger = options?.logger ?? mockServices.logger.mock();
|
||||
const db =
|
||||
options?.db ??
|
||||
(await DatabaseManager.fromConfig(config, { logger })
|
||||
.forPlugin('catalog')
|
||||
.getClient());
|
||||
|
||||
await applyDatabaseMigrations(db);
|
||||
await applyDatabaseMigrations(options.db);
|
||||
|
||||
const catalogDatabase = new DefaultCatalogDatabase({
|
||||
database: db,
|
||||
database: options.db,
|
||||
logger,
|
||||
});
|
||||
const providerDatabase = new DefaultProviderDatabase({
|
||||
database: db,
|
||||
database: options.db,
|
||||
logger,
|
||||
});
|
||||
const processingDatabase = new DefaultProcessingDatabase({
|
||||
database: db,
|
||||
database: options.db,
|
||||
logger,
|
||||
refreshInterval: () => 0.05,
|
||||
});
|
||||
@@ -281,9 +275,12 @@ class TestHarness {
|
||||
policy: EntityPolicies.allOf([]),
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
const stitcher = DefaultStitcher.fromConfig(config, { knex: db, logger });
|
||||
const stitcher = DefaultStitcher.fromConfig(config, {
|
||||
knex: options.db,
|
||||
logger,
|
||||
});
|
||||
const catalog = new DefaultEntitiesCatalog({
|
||||
database: db,
|
||||
database: options.db,
|
||||
logger,
|
||||
stitcher,
|
||||
disableRelationsCompatibility: options?.disableRelationsCompatibility,
|
||||
@@ -296,7 +293,7 @@ class TestHarness {
|
||||
config: new ConfigReader({}),
|
||||
logger,
|
||||
processingDatabase,
|
||||
knex: db,
|
||||
knex: options.db,
|
||||
orchestrator,
|
||||
stitcher,
|
||||
createHash: () => createHash('sha1'),
|
||||
@@ -333,7 +330,7 @@ class TestHarness {
|
||||
refresh,
|
||||
provider,
|
||||
proxyProgressTracker,
|
||||
db,
|
||||
options.db,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -444,10 +441,13 @@ class TestHarness {
|
||||
}
|
||||
|
||||
describe('Catalog Backend Integration', () => {
|
||||
const databases = TestDatabases.create({ ids: ['SQLITE_3'] });
|
||||
|
||||
it('should add entities and update errors', async () => {
|
||||
let triggerError = false;
|
||||
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
async processEntity(entity: Entity) {
|
||||
if (triggerError) {
|
||||
throw new Error('NOPE');
|
||||
@@ -525,6 +525,7 @@ describe('Catalog Backend Integration', () => {
|
||||
const generatedApis = ['api-1', 'api-2'];
|
||||
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
async processEntity(
|
||||
entity: Entity,
|
||||
location: LocationSpec,
|
||||
@@ -609,7 +610,9 @@ describe('Catalog Backend Integration', () => {
|
||||
});
|
||||
|
||||
it('should not replace matching provided entities', async () => {
|
||||
const harness = await TestHarness.create();
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
});
|
||||
|
||||
const entityA = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
@@ -669,6 +672,7 @@ describe('Catalog Backend Integration', () => {
|
||||
}
|
||||
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
async processEntity(
|
||||
entity: Entity,
|
||||
location: LocationSpec,
|
||||
@@ -788,7 +792,9 @@ describe('Catalog Backend Integration', () => {
|
||||
});
|
||||
|
||||
it('should reject insecure URLs', async () => {
|
||||
const harness = await TestHarness.create();
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
});
|
||||
|
||||
await harness.setInputEntities([
|
||||
{
|
||||
@@ -827,7 +833,9 @@ describe('Catalog Backend Integration', () => {
|
||||
});
|
||||
|
||||
it('should reject insecure location URLs', async () => {
|
||||
const harness = await TestHarness.create();
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
});
|
||||
|
||||
await harness.setInputEntities([
|
||||
{
|
||||
@@ -854,6 +862,7 @@ describe('Catalog Backend Integration', () => {
|
||||
|
||||
it('should return valid responses in raw JSON mode', async () => {
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
disableRelationsCompatibility: true,
|
||||
});
|
||||
|
||||
@@ -910,6 +919,7 @@ describe('Catalog Backend Integration', () => {
|
||||
const secondProvider = new TestProvider('second');
|
||||
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
additionalProviders: [firstProvider, secondProvider],
|
||||
});
|
||||
|
||||
@@ -1066,7 +1076,10 @@ describe('Catalog Backend Integration', () => {
|
||||
_emit: CatalogProcessorEmit,
|
||||
) => entity,
|
||||
);
|
||||
const harness = await TestHarness.create({ processEntity });
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
processEntity,
|
||||
});
|
||||
|
||||
processEntity.mockImplementation(async (entity, location, emit) => {
|
||||
if (entity.metadata.name === entityA.metadata.name) {
|
||||
@@ -1189,7 +1202,10 @@ describe('Catalog Backend Integration', () => {
|
||||
_emit: CatalogProcessorEmit,
|
||||
) => entity,
|
||||
);
|
||||
const harness = await TestHarness.create({ processEntity });
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
processEntity,
|
||||
});
|
||||
|
||||
processEntity.mockImplementation(async (entity, location, emit) => {
|
||||
if (entity.metadata.name === entityA.metadata.name) {
|
||||
@@ -1246,7 +1262,10 @@ describe('Catalog Backend Integration', () => {
|
||||
_emit: CatalogProcessorEmit,
|
||||
) => entity,
|
||||
);
|
||||
const harness = await TestHarness.create({ processEntity });
|
||||
const harness = await TestHarness.create({
|
||||
db: await databases.init('SQLITE_3'),
|
||||
processEntity,
|
||||
});
|
||||
|
||||
processEntity.mockImplementation(async (entity, location, emit) => {
|
||||
if (entity.metadata.name === entityA.metadata.name) {
|
||||
|
||||
@@ -32,7 +32,6 @@ export function locationSpecToMetadataName(location: LocationSpec) {
|
||||
return `generated-${hash}`;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function locationSpecToLocationEntity(opts: {
|
||||
location: LocationSpec;
|
||||
parentEntity?: Entity;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { parseEntityYaml } from './parse';
|
||||
@@ -14,51 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity, stringifyLocationRef } from '@backstage/catalog-model';
|
||||
import lodash from 'lodash';
|
||||
import yaml from 'yaml';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
import {
|
||||
CatalogProcessorParser,
|
||||
CatalogProcessorResult,
|
||||
processingResult,
|
||||
parseEntityYaml,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
|
||||
/** @public */
|
||||
export function* parseEntityYaml(
|
||||
data: Buffer,
|
||||
location: LocationSpec,
|
||||
): Iterable<CatalogProcessorResult> {
|
||||
let documents: yaml.Document.Parsed[];
|
||||
try {
|
||||
documents = yaml.parseAllDocuments(data.toString('utf8')).filter(d => d);
|
||||
} catch (e) {
|
||||
const loc = stringifyLocationRef(location);
|
||||
const message = `Failed to parse YAML at ${loc}, ${e}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const document of documents) {
|
||||
if (document.errors?.length) {
|
||||
const loc = stringifyLocationRef(location);
|
||||
const message = `YAML error at ${loc}, ${document.errors[0]}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
} else {
|
||||
const json = document.toJSON();
|
||||
if (lodash.isPlainObject(json)) {
|
||||
yield processingResult.entity(location, json as Entity);
|
||||
} else if (json === null) {
|
||||
// Ignore null values, these happen if there is an empty document in the
|
||||
// YAML file, for example if --- is added to the end of the file.
|
||||
} else {
|
||||
const message = `Expected object at root, got ${typeof json}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultEntityDataParser: CatalogProcessorParser =
|
||||
async function* defaultEntityDataParser({ data, location }) {
|
||||
for (const e of parseEntityYaml(data, location)) {
|
||||
|
||||
@@ -67,7 +67,9 @@
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/plugin-permission-node": "workspace:^",
|
||||
"@backstage/types": "workspace:^"
|
||||
"@backstage/types": "workspace:^",
|
||||
"lodash": "^4.17.21",
|
||||
"yaml": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
|
||||
@@ -297,6 +297,12 @@ export function locationSpecToLocationEntity(opts: {
|
||||
// @public
|
||||
export function locationSpecToMetadataName(location: LocationSpec_2): string;
|
||||
|
||||
// @public
|
||||
export function parseEntityYaml(
|
||||
data: string | Buffer,
|
||||
location: LocationSpec_2,
|
||||
): Iterable<CatalogProcessorResult>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type PlaceholderResolver = (
|
||||
params: PlaceholderResolverParams,
|
||||
|
||||
@@ -24,3 +24,4 @@ export type {
|
||||
LocationAnalyzer,
|
||||
ScmLocationAnalyzer,
|
||||
} from './types';
|
||||
export { parseEntityYaml } from './parse';
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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, stringifyLocationRef } from '@backstage/catalog-model';
|
||||
import lodash from 'lodash';
|
||||
import yaml from 'yaml';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
import { CatalogProcessorResult } from '../api/processor';
|
||||
import { processingResult } from '../api/processingResult';
|
||||
|
||||
/**
|
||||
* A helper function that parses a YAML file, properly handling multiple
|
||||
* documents in a single file.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* Each document is expected to be a valid Backstage entity. Each item in the
|
||||
* iterable is in the form of an entity result if the document seemed like a
|
||||
* valid object, or in the form of an error result if it seemed incorrect. This
|
||||
* way, you can choose to retain a partial result if you want.
|
||||
*
|
||||
* Note that this function does NOT perform any validation of the entities. No
|
||||
* assumptions whatsoever can be made on the emnitted entities except that they
|
||||
* are valid JSON objects.
|
||||
*/
|
||||
export function* parseEntityYaml(
|
||||
data: string | Buffer,
|
||||
location: LocationSpec,
|
||||
): Iterable<CatalogProcessorResult> {
|
||||
let documents: yaml.Document.Parsed[];
|
||||
try {
|
||||
documents = yaml
|
||||
.parseAllDocuments(
|
||||
typeof data === 'string' ? data : data.toString('utf8'),
|
||||
)
|
||||
.filter(d => d);
|
||||
} catch (e) {
|
||||
const loc = stringifyLocationRef(location);
|
||||
const message = `Failed to parse YAML at ${loc}, ${e}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const document of documents) {
|
||||
if (document.errors?.length) {
|
||||
const loc = stringifyLocationRef(location);
|
||||
const message = `YAML error at ${loc}, ${document.errors[0]}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
} else {
|
||||
const json = document.toJSON();
|
||||
if (lodash.isPlainObject(json)) {
|
||||
yield processingResult.entity(location, json as Entity);
|
||||
} else if (json === null) {
|
||||
// Ignore null values, these happen if there is an empty document in the
|
||||
// YAML file, for example if --- is added to the end of the file.
|
||||
} else {
|
||||
const message = `Expected object at root, got ${typeof json}`;
|
||||
yield processingResult.generalError(location, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6016,7 +6016,6 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend@workspace:plugins/catalog-backend"
|
||||
dependencies:
|
||||
"@backstage/backend-common": "npm:^0.25.0"
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-openapi-utils": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
@@ -6032,8 +6031,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:^"
|
||||
@@ -6189,7 +6186,9 @@ __metadata:
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
"@backstage/plugin-permission-node": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
lodash: "npm:^4.17.21"
|
||||
msw: "npm:^1.0.0"
|
||||
yaml: "npm:^2.0.0"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
||||
Reference in New Issue
Block a user