Use direct DB update for onConflict refresh instead of RefreshService

Simplify by updating refresh_state directly (next_update_at=now,
result_hash='') to force reprocessing, removing the need for
RefreshService wiring in the location store.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
This commit is contained in:
Fredrik Adelöw
2026-03-09 16:37:50 +01:00
parent ced73ba3a0
commit 67fa705515
5 changed files with 10 additions and 33 deletions
@@ -29,14 +29,13 @@ import {
EntityProviderConnection,
} from '@backstage/plugin-catalog-node';
import { locationSpecToLocationEntity } from '../util/conversion';
import { LocationInput, LocationStore, RefreshService } from '../service/types';
import { LocationInput, LocationStore } from '../service/types';
import {
ANNOTATION_ORIGIN_LOCATION,
CompoundEntityRef,
parseLocationRef,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { BackstageCredentials } from '@backstage/backend-plugin-api';
import {
CatalogScmEvent,
CatalogScmEventsService,
@@ -54,7 +53,6 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
private readonly db: Knex;
private readonly scmEvents: CatalogScmEventsService;
private readonly scmEventHandlingConfig: ScmEventHandlingConfig;
private refreshService?: RefreshService;
constructor(
db: Knex,
@@ -66,10 +64,6 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
this.scmEventHandlingConfig = scmEventHandlingConfig;
}
setRefreshService(refreshService: RefreshService): void {
this.refreshService = refreshService;
}
getProviderName(): string {
return 'DefaultLocationStore';
}
@@ -78,7 +72,6 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
input: LocationInput,
options?: {
onConflict?: 'refresh' | 'reject';
credentials?: BackstageCredentials;
},
): Promise<Location> {
let existed = false;
@@ -113,23 +106,15 @@ export class DefaultLocationStore implements LocationStore, EntityProvider {
});
if (existed) {
if (!this.refreshService) {
throw new InputError(
'onConflict refresh is not supported: no refresh service available',
);
}
if (!options?.credentials) {
throw new InputError(
'onConflict refresh requires credentials to be provided',
);
}
const entityRef = stringifyEntityRef(
locationSpecToLocationEntity({ location }),
);
await this.refreshService.refresh({
entityRef,
credentials: options.credentials,
});
await this.db<DbRefreshStateRow>('refresh_state')
.where({ entity_ref: entityRef })
.update({
next_update_at: this.db.fn.now(),
result_hash: '',
});
} else {
const entity = locationSpecToLocationEntity({ location });
await this.connection.applyMutation({
@@ -591,10 +591,6 @@ export class CatalogBuilder {
new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers),
permissionsService,
);
const innerRefreshService = new DefaultRefreshService({
database: catalogDatabase,
});
locationStore.setRefreshService(innerRefreshService);
const locationService = new AuthorizedLocationService(
new DefaultLocationService(locationStore, orchestrator, {
allowedLocationTypes: this.allowedLocationType,
@@ -602,7 +598,7 @@ export class CatalogBuilder {
permissionsService,
);
const refreshService = new AuthorizedRefreshService(
innerRefreshService,
new DefaultRefreshService({ database: catalogDatabase }),
permissionsService,
);
@@ -315,7 +315,7 @@ describe('DefaultLocationServiceTest', () => {
).rejects.toThrow(InputError);
});
it('should pass onConflict and credentials through to store', async () => {
it('should pass onConflict through to store', async () => {
const locationSpec = {
type: 'url',
target: 'https://backstage.io/catalog-info.yaml',
@@ -326,10 +326,9 @@ describe('DefaultLocationServiceTest', () => {
...locationSpec,
});
const credentials = {} as any;
const result = await locationService.createLocation(locationSpec, false, {
onConflict: 'refresh',
credentials,
credentials: {} as any,
});
expect(result).toEqual({
@@ -338,7 +337,6 @@ describe('DefaultLocationServiceTest', () => {
});
expect(store.createLocation).toHaveBeenCalledWith(locationSpec, {
onConflict: 'refresh',
credentials,
});
});
@@ -72,7 +72,6 @@ export class DefaultLocationService implements LocationService {
}
const location = await this.store.createLocation(input, {
onConflict: options?.onConflict,
credentials: options?.credentials,
});
return { location, entities: [] };
}
@@ -89,7 +89,6 @@ export interface LocationStore {
location: LocationInput,
options?: {
onConflict?: 'refresh' | 'reject';
credentials?: BackstageCredentials;
},
): Promise<Location>;
listLocations(): Promise<Location[]>;