catalog-backend: enable config location live reloads

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-08-08 17:35:43 +02:00
parent 9b4604b38a
commit 03bb05af6d
4 changed files with 98 additions and 12 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Enabled live reload of locations configured in `catalog.locations`.
@@ -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;
};