diff --git a/.changeset/three-lies-explode.md b/.changeset/three-lies-explode.md new file mode 100644 index 0000000000..df3556d0ba --- /dev/null +++ b/.changeset/three-lies-explode.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fix bug in `getLocationByEntity` diff --git a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.test.ts b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.test.ts index 61eef2b8e5..dafd6e13d8 100644 --- a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.test.ts +++ b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.test.ts @@ -15,8 +15,15 @@ */ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { ANNOTATION_ORIGIN_LOCATION } from '@backstage/catalog-model'; import { v4 as uuid } from 'uuid'; import { applyDatabaseMigrations } from '../../database/migrations'; +import { + DbFinalEntitiesRow, + DbLocationsRow, + DbRefreshStateRow, + DbSearchRow, +} from '../../database/tables'; import { DefaultLocationStore } from './DefaultLocationStore'; jest.setTimeout(60_000); @@ -30,7 +37,7 @@ describe('DefaultLocationStore', () => { const connection = { applyMutation: jest.fn(), refresh: jest.fn() }; const store = new DefaultLocationStore(knex); await store.connect(connection); - return { store, connection }; + return { store, connection, knex }; } it.each(databases.eachSupportedId())( @@ -172,4 +179,59 @@ describe('DefaultLocationStore', () => { }, ); }); + + describe('getLocationByEntity', () => { + it.each(databases.eachSupportedId())( + 'loads correctly, %p', + async databaseId => { + const { store, knex } = await createLocationStore(databaseId); + + const entityId = uuid(); + const locationId = uuid(); + + await knex('refresh_state').insert({ + entity_id: entityId, + entity_ref: 'k:ns/n', + unprocessed_entity: '{}', + errors: '[]', + next_update_at: new Date(), + last_discovery_at: new Date(), + }); + + await knex('final_entities').insert({ + entity_id: entityId, + final_entity: '{}', + hash: 'hash', + last_updated_at: new Date(), + stitch_ticket: '', + }); + + await knex('search').insert({ + entity_id: entityId, + key: `metadata.annotations.${ANNOTATION_ORIGIN_LOCATION}`, + value: `url:https://example.com`, + }); + + await knex('locations').insert({ + id: locationId, + type: 'url', + target: 'https://example.com', + }); + + await expect( + store.getLocationByEntity({ kind: 'k', namespace: 'ns', name: 'n' }), + ).resolves.toEqual({ + id: locationId, + type: 'url', + target: 'https://example.com', + }); + + await expect( + store.getLocationByEntity({ kind: 'k', namespace: 'ns', name: 'n2' }), + ).rejects.toMatchInlineSnapshot( + `[NotFoundError: found no entity for ref k:ns/n2]`, + ); + }, + ); + }); }); diff --git a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts index ac506051d2..fcf576b93e 100644 --- a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts @@ -124,41 +124,40 @@ export class DefaultLocationStore implements LocationStore, EntityProvider { async getLocationByEntity(entityRef: CompoundEntityRef): Promise { const entityRefString = stringifyEntityRef(entityRef); - const [entity] = await this.db('refresh_state') + const [entityRow] = await this.db('refresh_state') .where({ entity_ref: entityRefString }) .select('entity_id') .limit(1); - if (!entity) { + if (!entityRow) { throw new NotFoundError(`found no entity for ref ${entityRefString}`); } - const [locationKeyValue] = await this.db('search') + const [searchRow] = await this.db('search') .where({ - entity_id: entity.entity_id, + entity_id: entityRow.entity_id, key: `metadata.annotations.${ANNOTATION_ORIGIN_LOCATION}`, }) .select('value') .limit(1); - if (!locationKeyValue) { + if (!searchRow?.value) { throw new NotFoundError( `found no origin annotation for ref ${entityRefString}`, ); } - const { type, target } = parseLocationRef(entityRefString); - // const kind, target = split[0], split[1]; - const [location] = await this.db('locations') + const { type, target } = parseLocationRef(searchRow.value); + const [locationRow] = await this.db('locations') .where({ type, target }) .select() .limit(1); - // select * from locations where type = 'split(prev)[0]' - if (!location) { + if (!locationRow) { throw new NotFoundError( `Found no location with type ${type} and target ${target}`, ); } - return location; + + return locationRow; } private get connection(): EntityProviderConnection {