diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts index 934ab3a1cc..b423b61041 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts @@ -19,6 +19,8 @@ import { CatalogProcessingEngine, EntityProvider, EntityMessage, + EntityProviderConnection, + EntityProviderMutation, ProcessingStateManager, CatalogProcessingOrchestrator, } from './types'; @@ -27,8 +29,13 @@ import { Logger } from 'winston'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { Stitcher } from './Stitcher'; +class Connection implements EntityProviderConnection { + constructor(private readonly stateManager: ProcessingStateManager) {} + + async applyMutation(mutation: EntityProviderMutation): Promise {} +} + export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { - private subscriptions: Subscription[] = []; private running: boolean = false; constructor( @@ -41,11 +48,7 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { async start() { for (const provider of this.entityProviders) { - const id = 'databaseProvider'; - const subscription = provider - .entityChange$() - .subscribe({ next: m => this.onNext(id, m) }); - this.subscriptions.push(subscription); + provider.connect(new Connection(this.stateManager)); } this.running = true; diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.ts b/plugins/catalog-backend/src/next/DefaultLocationStore.ts index b21fe9da02..a6b4adcca4 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationStore.ts @@ -16,24 +16,29 @@ import { LocationSpec, Location } from '@backstage/catalog-model'; import { Database } from '../database'; -import { LocationStore } from './types'; +import { + LocationStore, + EntityProvider, + EntityProviderConnection, +} from './types'; import { v4 as uuidv4 } from 'uuid'; +import { locationSpecToLocationEntity } from './util'; import { ConflictError } from '@backstage/errors'; -import { Observable } from '@backstage/core'; -import ObservableImpl from 'zen-observable'; export type LocationMessage = | { all: Location[] } | { added: Location[]; removed: Location[] }; -export class DefaultLocationStore implements LocationStore { - private subscribers = new Set< - ZenObservable.SubscriptionObserver - >(); +export class DefaultLocationStore implements LocationStore, EntityProvider { + private _connection: EntityProviderConnection | undefined; constructor(private readonly db: Database) {} createLocation(spec: LocationSpec): Promise { + if (!this.connection) { + throw new Error('location store is not initialized'); + } + return this.db.transaction(async tx => { // TODO: id should really be type and target combined and not a uuid. @@ -55,7 +60,11 @@ export class DefaultLocationStore implements LocationStore { target: spec.target, }); - this.notifyAddition(location); + await this.connection.applyMutation({ + type: 'delta', + added: [locationSpecToLocationEntity(location)], + removed: [], + }); return location; }); @@ -75,40 +84,33 @@ export class DefaultLocationStore implements LocationStore { } deleteLocation(id: string): Promise { + if (!this.connection) { + throw new Error('location store is not initialized'); + } + return this.db.transaction(async tx => { const location = await this.db.location(id); if (!location) { throw new ConflictError(`No location found with id: ${id}`); } await this.db.removeLocation(tx, id); - this.notifyDeletion(location); - }); - } - - private notifyAddition(location: Location) { - for (const subscriber of this.subscribers) { - subscriber.next({ - added: [location], - removed: [], - }); - } - } - - private notifyDeletion(location: Location) { - for (const subscriber of this.subscribers) { - subscriber.next({ + await this.connection.applyMutation({ + type: 'delta', added: [], - removed: [location], + removed: [locationSpecToLocationEntity(location)], }); - } + }); } - location$(): Observable { - return new ObservableImpl(subscriber => { - this.subscribers.add(subscriber); - return () => { - this.subscribers.delete(subscriber); - }; - }); + private get connection(): EntityProviderConnection { + if (!this._connection) { + throw new Error('location store is not initialized'); + } + + return this._connection; + } + + async connect(connection: EntityProviderConnection): Promise { + this._connection = connection; } } diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index c3e0a6bd9c..1796847164 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -272,11 +272,10 @@ export class NextCatalogBuilder { const entitiesCatalog = new NextEntitiesCatalog(dbClient); const locationStore = new DefaultLocationStore(db); - const dbLocationProvider = new DatabaseLocationProvider(locationStore); const stitcher = new Stitcher(dbClient, logger); const processingEngine = new DefaultCatalogProcessingEngine( logger, - [dbLocationProvider], // entityproviders + [locationStore], // entityproviders stateManager, orchestrator, stitcher, diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts index 0aeb290224..c3854d631f 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/next/types.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Entity, EntityName, @@ -21,7 +22,6 @@ import { EntityRelationSpec, } from '@backstage/catalog-model'; import { JsonObject } from '@backstage/config'; -import { Observable } from '@backstage/core'; // << nooo export interface LocationEntity { apiVersion: 'backstage.io/v1alpha1'; @@ -45,20 +45,11 @@ export interface LocationService { deleteLocation(id: string): Promise; } -export type EntityMessage = - | { all: Entity[] } - | { added: Entity[]; removed: EntityName[] }; - export interface LocationStore { - // extends EntityProvider createLocation(spec: LocationSpec): Promise; listLocations(): Promise; getLocation(id: string): Promise; deleteLocation(id: string): Promise; - - location$(): Observable< - { all: Location[] } | { added: Location[]; removed: Location[] } - >; } export interface CatalogProcessingEngine { @@ -66,8 +57,16 @@ export interface CatalogProcessingEngine { stop(): Promise; } +export type EntityProviderMutation = + | { type: 'full'; entities: Iterable } + | { type: 'delta'; added: Iterable; removed: Iterable }; + +export interface EntityProviderConnection { + applyMutation(mutation: EntityProviderMutation): Promise; +} + export interface EntityProvider { - entityChange$(): Observable; + connect(connection: EntityProviderConnection): Promise; } export type EntityProcessingRequest = {