mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
fix: add event emitting logic
This commit is contained in:
parent
04851f7048
commit
9a3165f8da
@ -12,10 +12,19 @@ import {
|
|||||||
IHookSettingsGroupLabelOperationMap,
|
IHookSettingsGroupLabelOperationMap,
|
||||||
} from '@nestjs/event-emitter';
|
} from '@nestjs/event-emitter';
|
||||||
import { InjectModel } from '@nestjs/mongoose';
|
import { InjectModel } from '@nestjs/mongoose';
|
||||||
import { Document, Model, Query, Types } from 'mongoose';
|
import {
|
||||||
|
Document,
|
||||||
|
FilterQuery,
|
||||||
|
Model,
|
||||||
|
Query,
|
||||||
|
Types,
|
||||||
|
UpdateQuery,
|
||||||
|
UpdateWithAggregationPipeline,
|
||||||
|
} from 'mongoose';
|
||||||
|
|
||||||
import { I18nService } from '@/i18n/services/i18n.service';
|
import { I18nService } from '@/i18n/services/i18n.service';
|
||||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
import { BaseRepository, EHook } from '@/utils/generics/base-repository';
|
||||||
|
import { TFilterQuery } from '@/utils/types/filter.types';
|
||||||
|
|
||||||
import { Setting } from '../schemas/setting.schema';
|
import { Setting } from '../schemas/setting.schema';
|
||||||
import { SettingType } from '../schemas/types';
|
import { SettingType } from '../schemas/types';
|
||||||
@ -30,48 +39,38 @@ export class SettingRepository extends BaseRepository<Setting> {
|
|||||||
super(eventEmitter, model, Setting);
|
super(eventEmitter, model, Setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the `Setting` document after it has been retrieved.
|
|
||||||
*
|
|
||||||
* Checks the `type` of the setting and validates the `value` field according to the type:
|
|
||||||
* - `text` expects a string.
|
|
||||||
* - `multiple_text` expects an array of strings.
|
|
||||||
* - `checkbox` expects a boolean.
|
|
||||||
*
|
|
||||||
* @param setting The `Setting` document to be validated.
|
|
||||||
*/
|
|
||||||
async preCreateValidate(
|
async preCreateValidate(
|
||||||
setting: Document<unknown, unknown, Setting> &
|
doc: Document<unknown, unknown, Setting> &
|
||||||
Setting & { _id: Types.ObjectId },
|
Setting & { _id: Types.ObjectId },
|
||||||
|
filterCriteria: FilterQuery<Setting>,
|
||||||
|
updates: UpdateWithAggregationPipeline | UpdateQuery<Setting>,
|
||||||
) {
|
) {
|
||||||
if (
|
this.validateSettingValue(doc.type, doc.value);
|
||||||
(setting.type === SettingType.text ||
|
if (filterCriteria && updates) {
|
||||||
setting.type === SettingType.textarea) &&
|
this.eventEmitter.emit(
|
||||||
typeof setting.value !== 'string' &&
|
`hook:setting:${EHook.preUpdateValidate}`,
|
||||||
setting.value !== null
|
filterCriteria,
|
||||||
) {
|
updates,
|
||||||
throw new Error('Setting Model : Value must be a string!');
|
);
|
||||||
} else if (setting.type === SettingType.multiple_text) {
|
}
|
||||||
const isStringArray =
|
}
|
||||||
Array.isArray(setting.value) &&
|
|
||||||
setting.value.every((v) => {
|
async preUpdateValidate(
|
||||||
return typeof v === 'string';
|
criteria: string | TFilterQuery<Setting>,
|
||||||
});
|
dto: UpdateQuery<Setting>,
|
||||||
if (!isStringArray) {
|
filterCriteria: FilterQuery<Setting>,
|
||||||
throw new Error('Setting Model : Value must be a string array!');
|
updates: UpdateWithAggregationPipeline | UpdateQuery<Setting>,
|
||||||
}
|
): Promise<void> {
|
||||||
} else if (
|
const payload = dto.$set ? dto.$set : dto;
|
||||||
setting.type === SettingType.checkbox &&
|
if (typeof payload.value !== 'undefined') {
|
||||||
typeof setting.value !== 'boolean' &&
|
const { type } =
|
||||||
setting.value !== null
|
'type' in payload ? payload : await this.findOne(criteria);
|
||||||
) {
|
this.validateSettingValue(type, payload.value);
|
||||||
throw new Error('Setting Model : Value must be a boolean!');
|
this.eventEmitter.emit(
|
||||||
} else if (
|
`hook:setting:${EHook.preUpdateValidate}`,
|
||||||
setting.type === SettingType.number &&
|
filterCriteria,
|
||||||
typeof setting.value !== 'number' &&
|
updates,
|
||||||
setting.value !== null
|
);
|
||||||
) {
|
|
||||||
throw new Error('Setting Model : Value must be a number!');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,4 +99,45 @@ export class SettingRepository extends BaseRepository<Setting> {
|
|||||||
// Sync global settings var
|
// Sync global settings var
|
||||||
this.eventEmitter.emit(`hook:${group}:${label}`, setting);
|
this.eventEmitter.emit(`hook:${group}:${label}`, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the `Setting` document after it has been retrieved.
|
||||||
|
*
|
||||||
|
* Checks the `type` of the setting and validates the `value` field according to the type:
|
||||||
|
* - `text` expects a string.
|
||||||
|
* - `multiple_text` expects an array of strings.
|
||||||
|
* - `checkbox` expects a boolean.
|
||||||
|
*
|
||||||
|
* @param setting The `Setting` document to be validated.
|
||||||
|
*/
|
||||||
|
private validateSettingValue(type: SettingType, value: any) {
|
||||||
|
if (
|
||||||
|
(type === SettingType.text || type === SettingType.textarea) &&
|
||||||
|
typeof value !== 'string' &&
|
||||||
|
value !== null
|
||||||
|
) {
|
||||||
|
throw new Error('Setting Model : Value must be a string!');
|
||||||
|
} else if (type === SettingType.multiple_text) {
|
||||||
|
const isStringArray =
|
||||||
|
Array.isArray(value) &&
|
||||||
|
value.every((v) => {
|
||||||
|
return typeof v === 'string';
|
||||||
|
});
|
||||||
|
if (!isStringArray) {
|
||||||
|
throw new Error('Setting Model : Value must be a string array!');
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
type === SettingType.checkbox &&
|
||||||
|
typeof value !== 'boolean' &&
|
||||||
|
value !== null
|
||||||
|
) {
|
||||||
|
throw new Error('Setting Model : Value must be a boolean!');
|
||||||
|
} else if (
|
||||||
|
type === SettingType.number &&
|
||||||
|
typeof value !== 'number' &&
|
||||||
|
value !== null
|
||||||
|
) {
|
||||||
|
throw new Error('Setting Model : Value must be a number!');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
import { ClassTransformOptions, plainToClass } from 'class-transformer';
|
import { ClassTransformOptions, plainToClass } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
Document,
|
Document,
|
||||||
|
FilterQuery,
|
||||||
FlattenMaps,
|
FlattenMaps,
|
||||||
HydratedDocument,
|
HydratedDocument,
|
||||||
Model,
|
Model,
|
||||||
@ -48,6 +49,8 @@ export enum EHook {
|
|||||||
postUpdateMany = 'postUpdateMany',
|
postUpdateMany = 'postUpdateMany',
|
||||||
postDelete = 'postDelete',
|
postDelete = 'postDelete',
|
||||||
postCreateValidate = 'postCreateValidate',
|
postCreateValidate = 'postCreateValidate',
|
||||||
|
preUpdateValidate = 'preUpdateValidate',
|
||||||
|
postUpdateValidate = 'postUpdateValidate',
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseRepository<
|
export abstract class BaseRepository<
|
||||||
@ -460,6 +463,10 @@ export abstract class BaseRepository<
|
|||||||
new: true,
|
new: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const filterCriteria = query.getFilter();
|
||||||
|
const queryUpdates = query.getUpdate();
|
||||||
|
await this.preUpdateValidate(criteria, dto, filterCriteria, queryUpdates);
|
||||||
|
await this.postUpdateValidate(criteria, dto, filterCriteria, queryUpdates);
|
||||||
return await this.executeOne(query, this.cls);
|
return await this.executeOne(query, this.cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +489,11 @@ export abstract class BaseRepository<
|
|||||||
return await this.model.deleteMany(criteria);
|
return await this.model.deleteMany(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
async preCreateValidate(_doc: HydratedDocument<T>): Promise<void> {
|
async preCreateValidate(
|
||||||
|
_doc: HydratedDocument<T>,
|
||||||
|
_filterCriteria?: FilterQuery<T>,
|
||||||
|
_updates?: UpdateWithAggregationPipeline | UpdateQuery<T>,
|
||||||
|
): Promise<void> {
|
||||||
// Nothing ...
|
// Nothing ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,6 +501,24 @@ export abstract class BaseRepository<
|
|||||||
// Nothing ...
|
// Nothing ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async preUpdateValidate<D extends Partial<U>>(
|
||||||
|
_criteria: string | TFilterQuery<T>,
|
||||||
|
_dto: UpdateQuery<D>,
|
||||||
|
_filterCriteria: FilterQuery<T>,
|
||||||
|
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
|
||||||
|
): Promise<void> {
|
||||||
|
// Nothing ...
|
||||||
|
}
|
||||||
|
|
||||||
|
async postUpdateValidate<D extends Partial<U>>(
|
||||||
|
_criteria: string | TFilterQuery<T>,
|
||||||
|
_dto: UpdateQuery<D>,
|
||||||
|
_filterCriteria: FilterQuery<T>,
|
||||||
|
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
|
||||||
|
): Promise<void> {
|
||||||
|
// Nothing ...
|
||||||
|
}
|
||||||
|
|
||||||
async preCreate(_doc: HydratedDocument<T>): Promise<void> {
|
async preCreate(_doc: HydratedDocument<T>): Promise<void> {
|
||||||
// Nothing ...
|
// Nothing ...
|
||||||
}
|
}
|
||||||
|
|||||||
12
api/types/event-emitter.d.ts
vendored
12
api/types/event-emitter.d.ts
vendored
@ -6,7 +6,7 @@
|
|||||||
* 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 type { Document, Query } from 'mongoose';
|
import type { Document, FilterQuery, Query } from 'mongoose';
|
||||||
import { type Socket } from 'socket.io';
|
import { type Socket } from 'socket.io';
|
||||||
|
|
||||||
import { type BotStats } from '@/analytics/schemas/bot-stats.schema';
|
import { type BotStats } from '@/analytics/schemas/bot-stats.schema';
|
||||||
@ -207,6 +207,10 @@ declare module '@nestjs/event-emitter' {
|
|||||||
|
|
||||||
type TPostUpdate<T> = THydratedDocument<T>;
|
type TPostUpdate<T> = THydratedDocument<T>;
|
||||||
|
|
||||||
|
type TPreUpdateValidate<T> = FilterQuery<T>;
|
||||||
|
|
||||||
|
type TPostUpdateValidate<T> = THydratedDocument<T>;
|
||||||
|
|
||||||
type TPostDelete = DeleteResult;
|
type TPostDelete = DeleteResult;
|
||||||
|
|
||||||
type TPostUnion<T> =
|
type TPostUnion<T> =
|
||||||
@ -269,6 +273,12 @@ declare module '@nestjs/event-emitter' {
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
[EHook.postDelete]: TPostDelete;
|
[EHook.postDelete]: TPostDelete;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
[EHook.preUpdateValidate]: TPreUpdateValidate<T>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
[EHook.postUpdateValidate]: TPostUpdateValidate<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TNormalizedHook<E extends keyof IHookEntityOperationMap, O> = Extract<
|
type TNormalizedHook<E extends keyof IHookEntityOperationMap, O> = Extract<
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user