mirror of
https://github.com/hexastack/hexabot
synced 2025-01-22 18:45:57 +00:00
fix: add event emitting logic
This commit is contained in:
parent
04851f7048
commit
9a3165f8da
@ -12,10 +12,19 @@ import {
|
||||
IHookSettingsGroupLabelOperationMap,
|
||||
} from '@nestjs/event-emitter';
|
||||
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 { 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 { SettingType } from '../schemas/types';
|
||||
@ -30,48 +39,38 @@ export class SettingRepository extends BaseRepository<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(
|
||||
setting: Document<unknown, unknown, Setting> &
|
||||
doc: Document<unknown, unknown, Setting> &
|
||||
Setting & { _id: Types.ObjectId },
|
||||
filterCriteria: FilterQuery<Setting>,
|
||||
updates: UpdateWithAggregationPipeline | UpdateQuery<Setting>,
|
||||
) {
|
||||
if (
|
||||
(setting.type === SettingType.text ||
|
||||
setting.type === SettingType.textarea) &&
|
||||
typeof setting.value !== 'string' &&
|
||||
setting.value !== null
|
||||
) {
|
||||
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) => {
|
||||
return typeof v === 'string';
|
||||
});
|
||||
if (!isStringArray) {
|
||||
throw new Error('Setting Model : Value must be a string array!');
|
||||
}
|
||||
} else if (
|
||||
setting.type === SettingType.checkbox &&
|
||||
typeof setting.value !== 'boolean' &&
|
||||
setting.value !== null
|
||||
) {
|
||||
throw new Error('Setting Model : Value must be a boolean!');
|
||||
} else if (
|
||||
setting.type === SettingType.number &&
|
||||
typeof setting.value !== 'number' &&
|
||||
setting.value !== null
|
||||
) {
|
||||
throw new Error('Setting Model : Value must be a number!');
|
||||
this.validateSettingValue(doc.type, doc.value);
|
||||
if (filterCriteria && updates) {
|
||||
this.eventEmitter.emit(
|
||||
`hook:setting:${EHook.preUpdateValidate}`,
|
||||
filterCriteria,
|
||||
updates,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async preUpdateValidate(
|
||||
criteria: string | TFilterQuery<Setting>,
|
||||
dto: UpdateQuery<Setting>,
|
||||
filterCriteria: FilterQuery<Setting>,
|
||||
updates: UpdateWithAggregationPipeline | UpdateQuery<Setting>,
|
||||
): Promise<void> {
|
||||
const payload = dto.$set ? dto.$set : dto;
|
||||
if (typeof payload.value !== 'undefined') {
|
||||
const { type } =
|
||||
'type' in payload ? payload : await this.findOne(criteria);
|
||||
this.validateSettingValue(type, payload.value);
|
||||
this.eventEmitter.emit(
|
||||
`hook:setting:${EHook.preUpdateValidate}`,
|
||||
filterCriteria,
|
||||
updates,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,4 +99,45 @@ export class SettingRepository extends BaseRepository<Setting> {
|
||||
// Sync global settings var
|
||||
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 {
|
||||
Document,
|
||||
FilterQuery,
|
||||
FlattenMaps,
|
||||
HydratedDocument,
|
||||
Model,
|
||||
@ -48,6 +49,8 @@ export enum EHook {
|
||||
postUpdateMany = 'postUpdateMany',
|
||||
postDelete = 'postDelete',
|
||||
postCreateValidate = 'postCreateValidate',
|
||||
preUpdateValidate = 'preUpdateValidate',
|
||||
postUpdateValidate = 'postUpdateValidate',
|
||||
}
|
||||
|
||||
export abstract class BaseRepository<
|
||||
@ -460,6 +463,10 @@ export abstract class BaseRepository<
|
||||
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);
|
||||
}
|
||||
|
||||
@ -482,7 +489,11 @@ export abstract class BaseRepository<
|
||||
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 ...
|
||||
}
|
||||
|
||||
@ -490,6 +501,24 @@ export abstract class BaseRepository<
|
||||
// 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> {
|
||||
// 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).
|
||||
*/
|
||||
|
||||
import type { Document, Query } from 'mongoose';
|
||||
import type { Document, FilterQuery, Query } from 'mongoose';
|
||||
import { type Socket } from 'socket.io';
|
||||
|
||||
import { type BotStats } from '@/analytics/schemas/bot-stats.schema';
|
||||
@ -207,6 +207,10 @@ declare module '@nestjs/event-emitter' {
|
||||
|
||||
type TPostUpdate<T> = THydratedDocument<T>;
|
||||
|
||||
type TPreUpdateValidate<T> = FilterQuery<T>;
|
||||
|
||||
type TPostUpdateValidate<T> = THydratedDocument<T>;
|
||||
|
||||
type TPostDelete = DeleteResult;
|
||||
|
||||
type TPostUnion<T> =
|
||||
@ -269,6 +273,12 @@ declare module '@nestjs/event-emitter' {
|
||||
}
|
||||
| {
|
||||
[EHook.postDelete]: TPostDelete;
|
||||
}
|
||||
| {
|
||||
[EHook.preUpdateValidate]: TPreUpdateValidate<T>;
|
||||
}
|
||||
| {
|
||||
[EHook.postUpdateValidate]: TPostUpdateValidate<T>;
|
||||
};
|
||||
|
||||
type TNormalizedHook<E extends keyof IHookEntityOperationMap, O> = Extract<
|
||||
|
Loading…
Reference in New Issue
Block a user