mirror of
https://github.com/hexastack/hexabot
synced 2025-06-11 00:46:37 +00:00
feat: restrict deletion for context vars that are in use
This commit is contained in:
parent
aa25ce6b03
commit
bbba54b2c1
@ -137,7 +137,7 @@ describe('ContextVarController', () => {
|
|||||||
contextVarController.deleteOne(contextVarToDelete.id),
|
contextVarController.deleteOne(contextVarToDelete.id),
|
||||||
).rejects.toThrow(
|
).rejects.toThrow(
|
||||||
new NotFoundException(
|
new NotFoundException(
|
||||||
`ContextVar with ID ${contextVarToDelete.id} not found`,
|
`Context var with ID ${contextVarToDelete.id} not found.`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -6,21 +6,70 @@
|
|||||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import {
|
||||||
|
ForbiddenException,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
Optional,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { InjectModel } from '@nestjs/mongoose';
|
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 { ContextVar } from '../schemas/context-var.schema';
|
import { ContextVar } from '../schemas/context-var.schema';
|
||||||
|
import { BlockService } from '../services/block.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ContextVarRepository extends BaseRepository<ContextVar> {
|
export class ContextVarRepository extends BaseRepository<ContextVar> {
|
||||||
|
private readonly blockService: BlockService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly eventEmitter: EventEmitter2,
|
readonly eventEmitter: EventEmitter2,
|
||||||
@InjectModel(ContextVar.name) readonly model: Model<ContextVar>,
|
@InjectModel(ContextVar.name) readonly model: Model<ContextVar>,
|
||||||
|
@Optional() blockService?: BlockService,
|
||||||
) {
|
) {
|
||||||
super(eventEmitter, model, ContextVar);
|
super(eventEmitter, model, ContextVar);
|
||||||
|
this.blockService = blockService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-processing logic before deleting a context var.
|
||||||
|
* It avoids deleting a context var if its unique name is used in blocks within the capture_vars array.
|
||||||
|
* If the context var is found in blocks, the block IDs are returned in the exception message.
|
||||||
|
*
|
||||||
|
* @param query - The delete query.
|
||||||
|
* @param criteria - The filter criteria for finding context vars to delete.
|
||||||
|
*/
|
||||||
|
async preDelete(
|
||||||
|
_query: Query<
|
||||||
|
DeleteResult,
|
||||||
|
Document<ContextVar, any, any>,
|
||||||
|
unknown,
|
||||||
|
ContextVar,
|
||||||
|
'deleteOne' | 'deleteMany'
|
||||||
|
>,
|
||||||
|
criteria: TFilterQuery<ContextVar>,
|
||||||
|
) {
|
||||||
|
const ids = Array.isArray(criteria._id) ? criteria._id : [criteria._id];
|
||||||
|
|
||||||
|
for (const id of ids) {
|
||||||
|
const contextVar = await this.findOne({ _id: id });
|
||||||
|
if (!contextVar) {
|
||||||
|
throw new NotFoundException(`Context var with ID ${id} not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const associatedBlocks = await this.blockService?.find({
|
||||||
|
capture_vars: { $elemMatch: { context_var: contextVar.name } },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (associatedBlocks?.length > 0) {
|
||||||
|
const blockIds = associatedBlocks.map((block) => block.id).join(', ');
|
||||||
|
throw new ForbiddenException(
|
||||||
|
`Context var "${contextVar.name}" is associated with the following block(s): ${blockIds} and cannot be deleted.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
|||||||
import { THydratedDocument } from 'mongoose';
|
import { THydratedDocument } from 'mongoose';
|
||||||
|
|
||||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||||
|
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||||
|
|
||||||
@Schema({ timestamps: true })
|
@Schema({ timestamps: true })
|
||||||
export class ContextVar extends BaseSchema {
|
export class ContextVar extends BaseSchema {
|
||||||
@ -40,10 +41,10 @@ export class ContextVar extends BaseSchema {
|
|||||||
permanent?: boolean;
|
permanent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContextVarModel: ModelDefinition = {
|
export const ContextVarModel: ModelDefinition = LifecycleHookManager.attach({
|
||||||
name: ContextVar.name,
|
name: ContextVar.name,
|
||||||
schema: SchemaFactory.createForClass(ContextVar),
|
schema: SchemaFactory.createForClass(ContextVar),
|
||||||
};
|
});
|
||||||
|
|
||||||
export type ContextVarDocument = THydratedDocument<ContextVar>;
|
export type ContextVarDocument = THydratedDocument<ContextVar>;
|
||||||
|
|
||||||
|
@ -138,8 +138,7 @@ export const Categories = () => {
|
|||||||
if (selectedCategories.length > 0) {
|
if (selectedCategories.length > 0) {
|
||||||
deleteCategories(selectedCategories), setSelectedCategories([]);
|
deleteCategories(selectedCategories), setSelectedCategories([]);
|
||||||
deleteDialogCtl.closeDialog();
|
deleteDialogCtl.closeDialog();
|
||||||
}
|
} else if (deleteDialogCtl?.data) {
|
||||||
if (deleteDialogCtl?.data) {
|
|
||||||
{
|
{
|
||||||
deleteCategory(deleteDialogCtl.data);
|
deleteCategory(deleteDialogCtl.data);
|
||||||
deleteDialogCtl.closeDialog();
|
deleteDialogCtl.closeDialog();
|
||||||
|
@ -63,8 +63,8 @@ export const ContextVars = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { mutateAsync: deleteContextVar } = useDelete(EntityType.CONTEXT_VAR, {
|
const { mutateAsync: deleteContextVar } = useDelete(EntityType.CONTEXT_VAR, {
|
||||||
onError: () => {
|
onError: (error) => {
|
||||||
toast.error(t("message.internal_server_error"));
|
toast.error(error);
|
||||||
},
|
},
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
deleteDialogCtl.closeDialog();
|
deleteDialogCtl.closeDialog();
|
||||||
@ -76,7 +76,7 @@ export const ContextVars = () => {
|
|||||||
EntityType.CONTEXT_VAR,
|
EntityType.CONTEXT_VAR,
|
||||||
{
|
{
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
toast.error(error.message || t("message.internal_server_error"));
|
toast.error(error);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
deleteDialogCtl.closeDialog();
|
deleteDialogCtl.closeDialog();
|
||||||
|
Loading…
Reference in New Issue
Block a user