mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
fix: nlp sample inconsistency when language is deleted
This commit is contained in:
parent
82dd888f2f
commit
6652629995
@ -9,6 +9,7 @@
|
||||
|
||||
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
@ -63,6 +64,7 @@ describe('LanguageController', () => {
|
||||
},
|
||||
},
|
||||
LoggerService,
|
||||
EventEmitter2,
|
||||
],
|
||||
}).compile();
|
||||
languageService = module.get<LanguageService>(LanguageService);
|
||||
|
@ -8,16 +8,46 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Language } from '../schemas/language.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageRepository extends BaseRepository<Language> {
|
||||
constructor(@InjectModel(Language.name) readonly model: Model<Language>) {
|
||||
constructor(
|
||||
@InjectModel(Language.name) readonly model: Model<Language>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-delete hook that triggers before an language is deleted.
|
||||
*
|
||||
* @param query The query used to delete the language.
|
||||
* @param criteria The filter criteria used to find the language for deletion.
|
||||
*/
|
||||
async preDelete(
|
||||
_query: Query<
|
||||
DeleteResult,
|
||||
Document<Language, any, any>,
|
||||
unknown,
|
||||
Language,
|
||||
'deleteOne' | 'deleteMany'
|
||||
>,
|
||||
criteria: TFilterQuery<Language>,
|
||||
): Promise<void> {
|
||||
if (criteria._id) {
|
||||
const language = await this.findOne(
|
||||
typeof criteria === 'string' ? { _id: criteria } : criteria,
|
||||
);
|
||||
this.eventEmitter.emit('hook:language:delete', language);
|
||||
} else {
|
||||
throw new Error('Attempted to delete language using unknown criteria');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Language extends BaseSchema {
|
||||
@ -41,10 +42,10 @@ export class Language extends BaseSchema {
|
||||
isRTL?: boolean;
|
||||
}
|
||||
|
||||
export const LanguageModel: ModelDefinition = {
|
||||
export const LanguageModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
name: Language.name,
|
||||
schema: SchemaFactory.createForClass(Language),
|
||||
};
|
||||
});
|
||||
|
||||
export type LanguageDocument = THydratedDocument<Language>;
|
||||
|
||||
|
@ -49,15 +49,15 @@ export class NlpSampleStub extends BaseSchema {
|
||||
@Prop({
|
||||
type: MongooseSchema.Types.ObjectId,
|
||||
ref: 'Language',
|
||||
required: true,
|
||||
required: false,
|
||||
})
|
||||
language: unknown;
|
||||
language: unknown | null;
|
||||
}
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class NlpSample extends NlpSampleStub {
|
||||
@Transform(({ obj }) => obj.language.toString())
|
||||
language: string;
|
||||
language: string | null;
|
||||
|
||||
@Exclude()
|
||||
entities?: never;
|
||||
@ -66,7 +66,7 @@ export class NlpSample extends NlpSampleStub {
|
||||
@Schema({ timestamps: true })
|
||||
export class NlpSampleFull extends NlpSampleStub {
|
||||
@Type(() => Language)
|
||||
language: Language;
|
||||
language: Language | null;
|
||||
|
||||
@Type(() => NlpSampleEntity)
|
||||
entities: NlpSampleEntity[];
|
||||
|
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
import {
|
||||
CommonExample,
|
||||
@ -16,6 +17,7 @@ import {
|
||||
ExampleEntity,
|
||||
LookupTable,
|
||||
} from '@/extensions/helpers/nlp/default/types';
|
||||
import { Language } from '@/i18n/schemas/language.schema';
|
||||
import { LanguageService } from '@/i18n/services/language.service';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
@ -140,4 +142,21 @@ export class NlpSampleService extends BaseService<
|
||||
entity_synonyms,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* When a language gets deleted, we need to set related samples to null
|
||||
*
|
||||
* @param language The language that has been deleted.
|
||||
*/
|
||||
@OnEvent('hook:language:delete')
|
||||
async handleLanguageDelete(language: Language) {
|
||||
await this.updateMany(
|
||||
{
|
||||
language: language.id,
|
||||
},
|
||||
{
|
||||
language: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import AddIcon from "@mui/icons-material/Add";
|
||||
import { Button, Grid, Paper } from "@mui/material";
|
||||
import { GridColDef } from "@mui/x-data-grid";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryClient } from "react-query";
|
||||
|
||||
import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog";
|
||||
import { FilterTextfield } from "@/app-components/inputs/FilterTextfield";
|
||||
@ -21,6 +22,7 @@ import {
|
||||
} from "@/app-components/tables/columns/getColumns";
|
||||
import { renderHeader } from "@/app-components/tables/columns/renderHeader";
|
||||
import { DataGrid } from "@/app-components/tables/DataGrid";
|
||||
import { isSameEntity } from "@/hooks/crud/helpers";
|
||||
import { useDelete } from "@/hooks/crud/useDelete";
|
||||
import { useFind } from "@/hooks/crud/useFind";
|
||||
import { useUpdate } from "@/hooks/crud/useUpdate";
|
||||
@ -42,6 +44,7 @@ export const Languages = () => {
|
||||
const addDialogCtl = useDialog<ILanguage>(false);
|
||||
const editDialogCtl = useDialog<ILanguage>(false);
|
||||
const deleteDialogCtl = useDialog<string>(false);
|
||||
const queryClient = useQueryClient();
|
||||
const hasPermission = useHasPermission();
|
||||
const { onSearch, searchPayload } = useSearch<ILanguage>({
|
||||
$or: ["title", "code"],
|
||||
@ -66,6 +69,13 @@ export const Languages = () => {
|
||||
toast.error(t("message.internal_server_error"));
|
||||
},
|
||||
onSuccess() {
|
||||
queryClient.removeQueries({
|
||||
predicate: ({ queryKey }) => {
|
||||
const [_qType, qEntity] = queryKey;
|
||||
|
||||
return isSameEntity(qEntity, EntityType.NLP_SAMPLE);
|
||||
},
|
||||
});
|
||||
deleteDialogCtl.closeDialog();
|
||||
toast.success(t("message.item_delete_success"));
|
||||
},
|
||||
|
@ -104,7 +104,6 @@ export default function NlpSample() {
|
||||
{
|
||||
label: ActionColumnLabel.Edit,
|
||||
action: ({ entities, language, ...rest }) => {
|
||||
const lang = getLanguageFromCache(language) as ILanguage;
|
||||
const data: INlpDatasetSample = {
|
||||
...rest,
|
||||
entities: entities?.map((e) => {
|
||||
@ -119,7 +118,9 @@ export default function NlpSample() {
|
||||
entity: getNlpEntityFromCache(entity)?.name || "",
|
||||
};
|
||||
}),
|
||||
language: lang.code,
|
||||
language: language
|
||||
? (getLanguageFromCache(language) as ILanguage).code
|
||||
: null,
|
||||
};
|
||||
|
||||
editDialogCtl.openDialog(data);
|
||||
@ -186,9 +187,7 @@ export default function NlpSample() {
|
||||
maxWidth: 90,
|
||||
field: "language",
|
||||
renderCell: ({ row }) => {
|
||||
const language = getLanguageFromCache(row.language);
|
||||
|
||||
return language?.title;
|
||||
return row.language ? getLanguageFromCache(row.language)?.title : "";
|
||||
},
|
||||
headerName: t("label.language"),
|
||||
sortable: true,
|
||||
|
@ -24,7 +24,7 @@ export interface INlpSampleAttributes {
|
||||
trained?: boolean;
|
||||
type?: NlpSampleType;
|
||||
entities: string[];
|
||||
language: string;
|
||||
language: string | null;
|
||||
}
|
||||
|
||||
export interface INlpSampleStub
|
||||
@ -33,12 +33,12 @@ export interface INlpSampleStub
|
||||
|
||||
export interface INlpSample extends INlpSampleStub, IFormat<Format.BASIC> {
|
||||
entities: string[];
|
||||
language: string;
|
||||
language: string | null;
|
||||
}
|
||||
|
||||
export interface INlpSampleFull extends INlpSampleStub, IFormat<Format.FULL> {
|
||||
entities: INlpSampleEntity[];
|
||||
language: ILanguage;
|
||||
language: ILanguage | null;
|
||||
}
|
||||
|
||||
// Dataset Trainer
|
||||
|
Loading…
Reference in New Issue
Block a user