Merge pull request #13013 from stephane-mori/allow-unknown-location-type

Allow unknown typed location from being registered via the location service by configuration settings
This commit is contained in:
Ben Lambert
2022-08-25 16:02:02 +02:00
committed by GitHub
5 changed files with 72 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': minor
---
Allow unknown typed location from being registered via the location service by configuration settings
+1
View File
@@ -150,6 +150,7 @@ export class CatalogBuilder {
getDefaultProcessors(): CatalogProcessor[];
replaceEntityPolicies(policies: EntityPolicy[]): CatalogBuilder;
replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder;
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
setEntityDataParser(parser: CatalogProcessorParser): CatalogBuilder;
setFieldFormatValidators(validators: Partial<Validators>): CatalogBuilder;
setLocationAnalyzer(locationAnalyzer: LocationAnalyzer): CatalogBuilder;
@@ -147,7 +147,8 @@ export class CatalogBuilder {
maxSeconds: 150,
});
private locationAnalyzer: LocationAnalyzer | undefined = undefined;
private permissionRules: CatalogPermissionRule[];
private readonly permissionRules: CatalogPermissionRule[];
private allowedLocationType: string[];
/**
* Creates a catalog builder.
@@ -167,6 +168,7 @@ export class CatalogBuilder {
this.processorsReplace = false;
this.parser = undefined;
this.permissionRules = Object.values(catalogPermissionRules);
this.allowedLocationType = ['url'];
}
/**
@@ -363,6 +365,16 @@ export class CatalogBuilder {
this.permissionRules.push(...permissionRules.flat());
}
/**
* Sets up the allowed location types from being registered via the location service.
*
* @param allowedLocationTypes - the allowed location types
*/
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder {
this.allowedLocationType = allowedLocationTypes;
return this;
}
/**
* Wires up and returns all of the component parts of the catalog
*/
@@ -465,7 +477,9 @@ export class CatalogBuilder {
const locationAnalyzer =
this.locationAnalyzer ?? new RepoLocationAnalyzer(logger, integrations);
const locationService = new AuthorizedLocationService(
new DefaultLocationService(locationStore, orchestrator),
new DefaultLocationService(locationStore, orchestrator, {
allowedLocationTypes: this.allowedLocationType,
}),
permissionEvaluator,
);
const refreshService = new AuthorizedRefreshService(
@@ -144,6 +144,7 @@ describe('DefaultLocationServiceTest', () => {
store.listLocations.mockResolvedValueOnce([
{ id: '137', ...locationSpec },
]);
const result = await locationService.createLocation(
{ type: 'url', target: 'https://backstage.io/catalog-info.yaml' },
true,
@@ -225,6 +226,7 @@ describe('DefaultLocationServiceTest', () => {
store.listLocations.mockResolvedValueOnce([
{ id: '987', type: 'url', target: 'https://example.com' },
]);
const result = await locationService.createLocation(
{ type: 'url', target: 'https://backstage.io/catalog-info.yaml' },
true,
@@ -259,7 +261,41 @@ describe('DefaultLocationServiceTest', () => {
});
});
it('should not allow locations of unknown types', async () => {
it('should create location with unknown type if configuration allows it', async () => {
const locationSpec = {
type: 'unknown',
target: 'https://backstage.io/catalog-info.yaml',
};
store.createLocation.mockResolvedValue({
...locationSpec,
id: '123',
});
const locationServiceAllowingUnknownType = new DefaultLocationService(
store,
orchestrator,
{
allowedLocationTypes: ['url', 'unknown'],
},
);
await expect(
locationServiceAllowingUnknownType.createLocation(locationSpec, false),
).resolves.toEqual({
entities: [],
location: {
id: '123',
target: 'https://backstage.io/catalog-info.yaml',
type: 'unknown',
},
});
expect(store.createLocation).toBeCalledWith({
target: 'https://backstage.io/catalog-info.yaml',
type: 'unknown',
});
});
it('should not allow locations of unknown types by default', async () => {
await expect(
locationService.createLocation(
{
@@ -27,18 +27,29 @@ import { locationSpecToMetadataName } from '../util/conversion';
import { InputError } from '@backstage/errors';
import { DeferredEntity } from '@backstage/plugin-catalog-node';
export type DefaultLocationServiceOptions = {
allowedLocationTypes: string[];
};
export class DefaultLocationService implements LocationService {
constructor(
private readonly store: LocationStore,
private readonly orchestrator: CatalogProcessingOrchestrator,
private readonly options: DefaultLocationServiceOptions = {
allowedLocationTypes: ['url'],
},
) {}
async createLocation(
input: LocationInput,
dryRun: boolean,
): Promise<{ location: Location; entities: Entity[]; exists?: boolean }> {
if (input.type !== 'url') {
throw new InputError(`Registered locations must be of type 'url'`);
if (!this.options.allowedLocationTypes.includes(input.type)) {
throw new InputError(
`Registered locations must be of an allowed type ${JSON.stringify(
this.options.allowedLocationTypes,
)}`,
);
}
if (dryRun) {
return this.dryRunCreateLocation(input);