feat: delete unprocessed entity

Signed-off-by: Kurt King <kurtaking@gmail.com>
This commit is contained in:
Kurt King
2024-02-02 11:36:42 -07:00
committed by blam
parent 2c2f705fe1
commit 317085836d
3 changed files with 92 additions and 38 deletions
@@ -120,6 +120,16 @@ export class UnprocessedEntitiesModule {
owner: req.query.owner as string,
}),
);
});
})
.delete(
'/entities/unprocessed/delete/:entity_id',
async (request, response) => {
await this.database('refresh_state')
.where({ entity_id: request.params.entity_id })
.delete();
response.status(204).send();
},
);
}
}
@@ -54,6 +54,10 @@ export interface CatalogUnprocessedEntitiesApi {
* Returns a list of entities with state 'failed'
*/
failed(): Promise<CatalogUnprocessedEntitiesApiResponse>;
/**
* Deletes an entity from the refresh_state table
*/
delete(entity_id: string): Promise<void>;
}
/**
@@ -83,4 +87,10 @@ export class CatalogUnprocessedEntitiesClient
async failed(): Promise<CatalogUnprocessedEntitiesApiResponse> {
return await this.fetch('entities/unprocessed/failed');
}
async delete(entity_id: string): Promise<void> {
await this.fetch(`entities/unprocessed/delete/${entity_id}`, {
method: 'DELETE',
});
}
}
@@ -22,15 +22,18 @@ import {
Table,
TableColumn,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import IconButton from '@material-ui/core/IconButton';
import { Theme, makeStyles } from '@material-ui/core/styles';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
import { UnprocessedEntity } from '../types';
import { EntityDialog } from './EntityDialog';
import { catalogUnprocessedEntitiesApiRef } from '../api';
import useAsync from 'react-use/lib/useAsync';
import DeleteIcon from '@material-ui/icons/Delete';
const useStyles = makeStyles((theme: Theme) => ({
errorBox: {
@@ -94,6 +97,8 @@ export const FailedEntities = () => {
value: data,
} = useAsync(async () => await unprocessedApi.failed());
const [, setSelectedSearchTerm] = useState<string>('');
const unprocessedEntityApi = useApi(catalogUnprocessedEntitiesApiRef);
const alertApi = useApi(alertApiRef);
if (loading) {
return <Progress />;
@@ -102,6 +107,21 @@ export const FailedEntities = () => {
return <ErrorPanel error={error} />;
}
const handleDelete = async ({ id }: { id: string }) => {
try {
await unprocessedEntityApi.delete(id);
alertApi.post({
message: `Entity has been deleted`,
severity: 'success',
});
} catch (e) {
alertApi.post({
message: `Ran into an issue when deleting. Please try again later.`,
severity: 'error',
});
}
};
const columns: TableColumn[] = [
{
title: <Typography>entityRef</Typography>,
@@ -136,43 +156,57 @@ export const FailedEntities = () => {
<EntityDialog entity={rowData as UnprocessedEntity} />
),
},
{
title: <Typography>Actions</Typography>,
render: (rowData: UnprocessedEntity | {}) => {
return (
<IconButton
aria-label="delete"
onClick={async () =>
await handleDelete({
id: (rowData as UnprocessedEntity).entity_id,
})
}
>
<DeleteIcon fontSize="small" data-testid="delete-icon" />
</IconButton>
);
},
},
];
return (
<>
<Table
options={{ pageSize: 20, search: true }}
columns={columns}
data={data?.entities || []}
emptyContent={
<Typography className={classes.successMessage}>
No failed entities found
</Typography>
}
onSearchChange={(searchTerm: string) =>
setSelectedSearchTerm(searchTerm)
}
detailPanel={({ rowData }) => {
const errors = (rowData as UnprocessedEntity).errors;
return (
<>
{errors?.map(e => {
return (
<Box className={classes.errorBox}>
<Typography className={classes.errorTitle}>
{e.name}
</Typography>
<MarkdownContent content={e.message} />
<RenderErrorContext
error={e}
rowData={rowData as UnprocessedEntity}
/>
</Box>
);
})}
</>
);
}}
/>
</>
<Table
options={{ pageSize: 20, search: true }}
columns={columns}
data={data?.entities ?? []}
emptyContent={
<Typography className={classes.successMessage}>
No failed entities found
</Typography>
}
onSearchChange={(searchTerm: string) => setSelectedSearchTerm(searchTerm)}
detailPanel={({ rowData }) => {
const errors = (rowData as UnprocessedEntity).errors;
return (
<>
{errors?.map((e, idx) => {
return (
<Box key={idx} className={classes.errorBox}>
<Typography className={classes.errorTitle}>
{e.name}
</Typography>
<MarkdownContent content={e.message} />
<RenderErrorContext
error={e}
rowData={rowData as UnprocessedEntity}
/>
</Box>
);
})}
</>
);
}}
/>
);
};