diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 7094cfd1c2..43e208197f 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -469,6 +469,62 @@ describe('DefaultEntitiesCatalog', () => { expect(entities.length).toBe(0); }, ); + + it.each(databases.eachSupportedId())( + 'should return both target and targetRef for entities', + async databaseId => { + const { knex } = await createDatabase(databaseId); + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + relations: [{ type: 'r', targetRef: 'x:y/z' } as any], + }, + [], + ); + await addEntity( + knex, + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: {}, + relations: [ + { + type: 'r', + target: { kind: 'x', namespace: 'y', name: 'z' }, + } as any, + ], + }, + [], + ); + const catalog = new DefaultEntitiesCatalog(knex); + + const { entities } = await catalog.entities(); + + expect( + entities.find(e => e.metadata.name === 'one')!.relations, + ).toEqual([ + { + type: 'r', + targetRef: 'x:y/z', + target: { kind: 'x', namespace: 'y', name: 'z' }, + }, + ]); + expect( + entities.find(e => e.metadata.name === 'two')!.relations, + ).toEqual([ + { + type: 'r', + targetRef: 'x:y/z', + target: { kind: 'x', namespace: 'y', name: 'z' }, + }, + ]); + }, + ); }); describe('removeEntityByUid', () => { diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 31f98c9f0c..1e681a4f43 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { + Entity, + parseEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { InputError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import lodash from 'lodash'; @@ -200,6 +204,28 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { entities = entities.map(e => request.fields!(e)); } + // TODO(freben): This is added as a compatibility guarantee, until we can be + // sure that all adopters have re-stitched their entities so that the new + // targetRef field is present on them, and that they have stopped consuming + // the now-removed old field + // TODO(jhaals): Remove this in April 2021 + for (const entity of entities) { + if (entity.relations) { + for (const relation of entity.relations as any) { + if (!relation.targetRef && relation.target) { + // This is the case where an old-form entity, not yet stitched with + // the updated code, was in the database + relation.targetRef = stringifyEntityRef(relation.target); + } else if (!relation.target && relation.targetRef) { + // This is the case where a new-form entity, stitched with the + // updated code, was in the database but we still want to produce + // the old data shape as well for compatibility reasons + relation.target = parseEntityRef(relation.targetRef); + } + } + } + } + return { entities, pageInfo,