From 593632f0787c008d7ff8089bf7b05049a1cb44cc Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 19 Jan 2021 15:51:40 +0100 Subject: [PATCH] Block deleting entities from the bootstrap location in the unregister dialog and list the correct delete preview --- .changeset/cool-horses-applaud.md | 6 ++ .../UnregisterEntityDialog.tsx | 56 +++++++++++++++---- 2 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 .changeset/cool-horses-applaud.md diff --git a/.changeset/cool-horses-applaud.md b/.changeset/cool-horses-applaud.md new file mode 100644 index 0000000000..f934634225 --- /dev/null +++ b/.changeset/cool-horses-applaud.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Derive the list of to-deleted entities in the `UnregisterEntityDialog` from the `backstage.io/managed-by-origin-location` annotation. +The dialog also rejects deleting entities that are created by the `bootstrap:bootstrap` location. diff --git a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx index ce6e63da1b..0c52b24d90 100644 --- a/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx +++ b/plugins/catalog/src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Entity, LOCATION_ANNOTATION } from '@backstage/catalog-model'; +import { Entity, ORIGIN_LOCATION_ANNOTATION } from '@backstage/catalog-model'; import { alertApiRef, Progress, useApi } from '@backstage/core'; import { Button, @@ -32,6 +32,7 @@ import React from 'react'; import { useAsync } from 'react-use'; import { AsyncState } from 'react-use/lib/useAsync'; import { catalogApiRef } from '../../plugin'; +import { formatEntityRefTitle } from '../EntityRefLink'; type Props = { open: boolean; @@ -40,15 +41,30 @@ type Props = { entity: Entity; }; +class DeniedLocationException extends Error { + constructor(public readonly locationName: string) { + super(`You may not remove the location ${locationName}`); + this.name = 'DeniedLocationException'; + } +} + function useColocatedEntities(entity: Entity): AsyncState { const catalogApi = useApi(catalogApiRef); return useAsync(async () => { - const myLocation = entity.metadata.annotations?.[LOCATION_ANNOTATION]; + const myLocation = + entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION]; if (!myLocation) { return []; } + + if (myLocation === 'bootstrap:bootstrap') { + throw new DeniedLocationException(myLocation); + } + const response = await catalogApi.getEntities({ - filter: { [LOCATION_ANNOTATION]: myLocation }, + filter: { + [`metadata.annotations.${ORIGIN_LOCATION_ANNOTATION}`]: myLocation, + }, }); return response.items; }, [catalogApi, entity]); @@ -82,13 +98,25 @@ export const UnregisterEntityDialog = ({ Are you sure you want to unregister this entity? + {loading ? : null} + {error ? ( - {error.toString()} + {error instanceof DeniedLocationException ? ( + <> + You cannot unregister this entity, since it originates from a + protected Backstage configuration (location + {`"${error.locationName}"`}). If you believe this is in error, + please contact your Backstage operator. + + ) : ( + error.toString() + )} ) : null} + {entities?.length ? ( <> @@ -96,9 +124,10 @@ export const UnregisterEntityDialog = ({
    - {entities.map(e => ( -
  • {e.metadata.name}
  • - ))} + {entities.map(e => { + const fullName = formatEntityRefTitle(e); + return
  • {fullName}
  • ; + })}
@@ -107,16 +136,21 @@ export const UnregisterEntityDialog = ({
  • - {entities[0]?.metadata.annotations?.[LOCATION_ANNOTATION]} + { + entities[0]?.metadata.annotations?.[ + ORIGIN_LOCATION_ANNOTATION + ] + }
+ + To undo, just re-register the entity in Backstage. + ) : null} - - To undo, just re-register the entity in Backstage. -
+