catalog-backend: enable config location live reloads
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { resolvePackagePath } from '@backstage/backend-common';
|
||||
import {
|
||||
resolvePackagePath,
|
||||
ObservableConfig,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import path from 'path';
|
||||
import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider';
|
||||
@@ -70,4 +73,64 @@ describe('ConfigLocationEntityProvider', () => {
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to observe the config', async () => {
|
||||
// Grab the subscriber function and use mutable config data to mock a config file change
|
||||
let subscriber: () => void;
|
||||
const mutableConfigData = {
|
||||
catalog: {
|
||||
locations: [{ type: 'url', target: 'https://github.com/a/a' }],
|
||||
},
|
||||
};
|
||||
|
||||
const mockConfig: ObservableConfig = Object.assign(
|
||||
new ConfigReader(mutableConfigData),
|
||||
{
|
||||
subscribe: (s: () => void) => {
|
||||
subscriber = s;
|
||||
return { unsubscribe: () => {} };
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const mockConnection = {
|
||||
applyMutation: jest.fn(),
|
||||
} as unknown as EntityProviderConnection;
|
||||
const locationProvider = new ConfigLocationEntityProvider(mockConfig);
|
||||
|
||||
await locationProvider.connect(mockConnection);
|
||||
|
||||
expect(mockConnection.applyMutation).toHaveBeenCalledWith({
|
||||
type: 'full',
|
||||
entities: [
|
||||
{
|
||||
entity: expect.objectContaining({
|
||||
spec: {
|
||||
target: 'https://github.com/a/a',
|
||||
type: 'url',
|
||||
},
|
||||
}),
|
||||
locationKey: 'url:https://github.com/a/a',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
mutableConfigData.catalog.locations[0].target = 'https://github.com/b/b';
|
||||
subscriber!();
|
||||
|
||||
expect(mockConnection.applyMutation).toHaveBeenCalledWith({
|
||||
type: 'full',
|
||||
entities: [
|
||||
{
|
||||
entity: expect.objectContaining({
|
||||
spec: {
|
||||
target: 'https://github.com/b/b',
|
||||
type: 'url',
|
||||
},
|
||||
}),
|
||||
locationKey: 'url:https://github.com/b/b',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,27 +15,49 @@
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { ObservableConfig } from '@backstage/backend-common';
|
||||
import path from 'path';
|
||||
import { getEntityLocationRef } from './processing/util';
|
||||
import { EntityProvider, EntityProviderConnection } from './types';
|
||||
import { locationSpecToLocationEntity } from './util';
|
||||
|
||||
export class ConfigLocationEntityProvider implements EntityProvider {
|
||||
private connection: EntityProviderConnection | undefined;
|
||||
|
||||
constructor(private readonly config: Config) {}
|
||||
constructor(private readonly config: Config | ObservableConfig) {}
|
||||
|
||||
getProviderName(): string {
|
||||
return 'ConfigLocationProvider';
|
||||
}
|
||||
|
||||
async connect(connection: EntityProviderConnection): Promise<void> {
|
||||
this.connection = connection;
|
||||
const entities = this.getEntitiesFromConfig();
|
||||
await connection.applyMutation({
|
||||
type: 'full',
|
||||
entities,
|
||||
});
|
||||
|
||||
if ('subscribe' in this.config) {
|
||||
let currentKey = JSON.stringify(entities);
|
||||
|
||||
this.config.subscribe(() => {
|
||||
const newEntities = this.getEntitiesFromConfig();
|
||||
const newKey = JSON.stringify(newEntities);
|
||||
|
||||
if (currentKey !== newKey) {
|
||||
currentKey = newKey;
|
||||
connection.applyMutation({
|
||||
type: 'full',
|
||||
entities: newEntities,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getEntitiesFromConfig() {
|
||||
const locationConfigs =
|
||||
this.config.getOptionalConfigArray('catalog.locations') ?? [];
|
||||
|
||||
const entities = locationConfigs.map(location => {
|
||||
return locationConfigs.map(location => {
|
||||
const type = location.getString('type');
|
||||
const target = location.getString('target');
|
||||
const entity = locationSpecToLocationEntity({
|
||||
@@ -45,10 +67,5 @@ export class ConfigLocationEntityProvider implements EntityProvider {
|
||||
const locationKey = getEntityLocationRef(entity);
|
||||
return { entity, locationKey };
|
||||
});
|
||||
|
||||
await this.connection.applyMutation({
|
||||
type: 'full',
|
||||
entities,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
ObservableConfig,
|
||||
PluginDatabaseManager,
|
||||
resolvePackagePath,
|
||||
UrlReader,
|
||||
@@ -79,7 +80,7 @@ import { Stitcher } from './stitching/Stitcher';
|
||||
export type CatalogEnvironment = {
|
||||
logger: Logger;
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
config: Config | ObservableConfig;
|
||||
reader: UrlReader;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user