Merge pull request #186 from Hexastack/178-request-add-bulk-delete-functionality-to-nlu-samples-list

feat: NLP samples bulk delete
This commit is contained in:
Med Marrouchi 2024-10-10 14:54:05 +01:00 committed by GitHub
commit ca196eb044
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 113 additions and 3 deletions

View File

@ -429,4 +429,45 @@ describe('NlpSampleController', () => {
expect(result).toEqual({ success: true });
});
});
describe('deleteMany', () => {
it('should delete multiple nlp samples', async () => {
const samplesToDelete = [
(
await nlpSampleService.findOne({
text: 'How much does a BMW cost?',
})
).id,
(
await nlpSampleService.findOne({
text: 'text1',
})
).id,
];
const result = await nlpSampleController.deleteMany(samplesToDelete);
expect(result.deletedCount).toEqual(samplesToDelete.length);
const remainingSamples = await nlpSampleService.find({
_id: { $in: samplesToDelete },
});
expect(remainingSamples.length).toBe(0);
});
it('should throw BadRequestException when no IDs are provided', async () => {
await expect(nlpSampleController.deleteMany([])).rejects.toThrow(
BadRequestException,
);
});
it('should throw NotFoundException when provided IDs do not exist', async () => {
const nonExistentIds = [
'614c1b2f58f4f04c876d6b8d',
'614c1b2f58f4f04c876d6b8e',
];
await expect(
nlpSampleController.deleteMany(nonExistentIds),
).rejects.toThrow(NotFoundException);
});
});
});

View File

@ -37,6 +37,7 @@ import { LanguageService } from '@/i18n/services/language.service';
import { CsrfInterceptor } from '@/interceptors/csrf.interceptor';
import { LoggerService } from '@/logger/logger.service';
import { BaseController } from '@/utils/generics/base-controller';
import { DeleteResult } from '@/utils/generics/base-repository';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { PageQueryPipe } from '@/utils/pagination/pagination-query.pipe';
import { PopulatePipe } from '@/utils/pipes/populate.pipe';
@ -321,6 +322,33 @@ export class NlpSampleController extends BaseController<
return result;
}
/**
* Deletes multiple NLP samples by their IDs.
* @param ids - IDs of NLP samples to be deleted.
* @returns A Promise that resolves to the deletion result.
*/
@CsrfCheck(true)
@Delete('')
@HttpCode(204)
async deleteMany(@Body('ids') ids: string[]): Promise<DeleteResult> {
if (!ids || ids.length === 0) {
throw new BadRequestException('No IDs provided for deletion.');
}
const deleteResult = await this.nlpSampleService.deleteMany({
_id: { $in: ids },
});
if (deleteResult.deletedCount === 0) {
this.logger.warn(
`Unable to delete NLP samples with provided IDs: ${ids}`,
);
throw new NotFoundException('NLP samples with provided IDs not found');
}
this.logger.log(`Successfully deleted NLP samples with IDs: ${ids}`);
return deleteResult;
}
/**
* Imports NLP samples from a CSV file.
*

View File

@ -20,7 +20,7 @@ import {
MenuItem,
Stack,
} from "@mui/material";
import { GridColDef } from "@mui/x-data-grid";
import { GridColDef, GridRowSelectionModel } from "@mui/x-data-grid";
import { useState } from "react";
import { DeleteDialog } from "@/app-components/dialogs";
@ -35,6 +35,7 @@ import {
import { renderHeader } from "@/app-components/tables/columns/renderHeader";
import { DataGrid } from "@/app-components/tables/DataGrid";
import { useDelete } from "@/hooks/crud/useDelete";
import { useDeleteMany } from "@/hooks/crud/useDeleteMany";
import { useFind } from "@/hooks/crud/useFind";
import { useGetFromCache } from "@/hooks/crud/useGet";
import { useConfig } from "@/hooks/useConfig";
@ -91,6 +92,20 @@ export default function NlpSample() {
toast.success(t("message.item_delete_success"));
},
});
const { mutateAsync: deleteNlpSamples } = useDeleteMany(
EntityType.NLP_SAMPLE,
{
onError: (error) => {
toast.error(error);
},
onSuccess: () => {
deleteDialogCtl.closeDialog();
setSelectedNlpSamples([]);
toast.success(t("message.item_delete_success"));
},
},
);
const [selectedNlpSamples, setSelectedNlpSamples] = useState<string[]>([]);
const { dataGridProps } = useFind(
{ entity: EntityType.NLP_SAMPLE, format: Format.FULL },
{
@ -242,6 +257,9 @@ export default function NlpSample() {
},
actionColumns,
];
const handleSelectionChange = (selection: GridRowSelectionModel) => {
setSelectedNlpSamples(selection as string[]);
};
return (
<Grid item xs={12}>
@ -249,7 +267,13 @@ export default function NlpSample() {
<DeleteDialog
{...deleteDialogCtl}
callback={() => {
if (deleteDialogCtl.data) deleteNlpSample(deleteDialogCtl.data);
if (selectedNlpSamples.length > 0) {
deleteNlpSamples(selectedNlpSamples);
setSelectedNlpSamples([]);
deleteDialogCtl.closeDialog();
} else if (deleteDialogCtl.data) {
deleteNlpSample(deleteDialogCtl.data);
}
}}
/>
<NlpImportDialog {...getDisplayDialogs(importDialogCtl)} />
@ -346,12 +370,29 @@ export default function NlpSample() {
{t("button.export")}
</Button>
) : null}
{selectedNlpSamples.length > 0 && (
<Grid item>
<Button
startIcon={<DeleteIcon />}
variant="contained"
color="error"
onClick={() => deleteDialogCtl.openDialog(undefined)}
>
{t("button.delete")}
</Button>
</Grid>
)}
</ButtonGroup>
</Grid>
</Grid>
<Grid mt={3}>
<DataGrid columns={columns} {...dataGridProps} />
<DataGrid
columns={columns}
{...dataGridProps}
checkboxSelection
onRowSelectionModelChange={handleSelectionChange}
/>
</Grid>
</Grid>
);