catalog-backend: include parent refs for deferred entities in cache key
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Fixed an bug in the entity processing caching that would prevent entities that were emitted during processing to be restored after being overridden.
|
||||
@@ -272,7 +272,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
const rows = await tx<DbRefreshStateReferencesRow>(
|
||||
'refresh_state_references',
|
||||
)
|
||||
.where({ target_entity_ref: options.entityRef })
|
||||
.whereIn('target_entity_ref', options.entityRefs)
|
||||
.select();
|
||||
|
||||
const entityRefs = rows.map(r => r.source_entity_ref!).filter(Boolean);
|
||||
|
||||
@@ -101,7 +101,7 @@ export type ListAncestorsResult = {
|
||||
};
|
||||
|
||||
export type ListParentsOptions = {
|
||||
entityRef: string;
|
||||
entityRefs: string[];
|
||||
};
|
||||
|
||||
export type ListParentsResult = {
|
||||
|
||||
@@ -215,7 +215,12 @@ export class DefaultCatalogProcessingEngine {
|
||||
const { entityRefs: parents } =
|
||||
await this.processingDatabase.transaction(tx =>
|
||||
this.processingDatabase.listParents(tx, {
|
||||
entityRef,
|
||||
entityRefs: [
|
||||
entityRef,
|
||||
...result.deferredEntities.map(e =>
|
||||
stringifyEntityRef(e.entity),
|
||||
),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1223,4 +1223,94 @@ describe('Catalog Backend Integration', () => {
|
||||
'component:default/b': withOutputFields(entityBOverride),
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve handle location key conflicts across processed and provided entities', async () => {
|
||||
const baseEntity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
annotations: {
|
||||
'backstage.io/managed-by-location': 'url:.',
|
||||
'backstage.io/managed-by-origin-location': 'url:.',
|
||||
},
|
||||
},
|
||||
};
|
||||
const entityA = merge({ metadata: { name: 'a' } }, baseEntity);
|
||||
const entityB = merge({ metadata: { name: 'b' } }, baseEntity);
|
||||
const entityBOverride = merge({ metadata: { override: true } }, entityB);
|
||||
|
||||
const processEntity = jest.fn(
|
||||
async (
|
||||
entity: Entity,
|
||||
_location: LocationSpec,
|
||||
_emit: CatalogProcessorEmit,
|
||||
) => entity,
|
||||
);
|
||||
const harness = await TestHarness.create({ processEntity });
|
||||
|
||||
processEntity.mockImplementation(async (entity, location, emit) => {
|
||||
if (entity.metadata.name === entityA.metadata.name) {
|
||||
emit(
|
||||
processingResult.entity(location, entityB, {
|
||||
locationKey: 'my-key',
|
||||
}),
|
||||
);
|
||||
}
|
||||
return entity;
|
||||
});
|
||||
|
||||
// Start with just A, which outputs B via processing
|
||||
await harness.setInputEntities([entityA]);
|
||||
await expect(harness.process()).resolves.toEqual({});
|
||||
|
||||
await expect(harness.getOutputEntities()).resolves.toEqual({
|
||||
'component:default/a': withOutputFields(entityA),
|
||||
'component:default/b': withOutputFields(entityB),
|
||||
});
|
||||
|
||||
// Now apply A and B', but since B already has a different location key, B' should not be used
|
||||
await harness.setInputEntities([entityA, entityBOverride]);
|
||||
await expect(harness.process()).resolves.toEqual({});
|
||||
|
||||
await expect(harness.getOutputEntities()).resolves.toEqual({
|
||||
'component:default/a': withOutputFields(entityA),
|
||||
'component:default/b': withOutputFields(entityB),
|
||||
});
|
||||
|
||||
// Stop emitting B from A
|
||||
processEntity.mockImplementation(async e => e);
|
||||
|
||||
// Apply A and B', but since B still has a location key set it should remain, but now orphaned
|
||||
await harness.setInputEntities([entityA, entityBOverride]);
|
||||
await expect(harness.process()).resolves.toEqual({});
|
||||
|
||||
await expect(harness.getOutputEntities()).resolves.toEqual({
|
||||
'component:default/a': withOutputFields(entityA),
|
||||
'component:default/b': withOutputFields(
|
||||
merge(entityB, {
|
||||
metadata: { annotations: { 'backstage.io/orphan': 'true' } },
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
// Apply A and B' but this time with the matching location key, causing B' to override B
|
||||
await harness.setInputEntities([
|
||||
entityA,
|
||||
{ ...entityBOverride, locationKey: 'my-key' },
|
||||
]);
|
||||
await expect(harness.process()).resolves.toEqual({});
|
||||
|
||||
await expect(harness.getOutputEntities()).resolves.toEqual({
|
||||
'component:default/a': withOutputFields(entityA),
|
||||
'component:default/b': withOutputFields(entityBOverride),
|
||||
});
|
||||
|
||||
// Finally, remove B', leaving just A
|
||||
await harness.setInputEntities([entityA]);
|
||||
await expect(harness.process()).resolves.toEqual({});
|
||||
|
||||
await expect(harness.getOutputEntities()).resolves.toEqual({
|
||||
'component:default/a': withOutputFields(entityA),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user