feat: delete unprocessed entity
Signed-off-by: Kurt King <kurtaking@gmail.com>
This commit is contained in:
@@ -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>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user