refactor(api): centrelize lifecycle-hook-manager types

This commit is contained in:
yassinedorbozgithub 2025-06-08 09:45:33 +01:00
parent 911d1fb3df
commit 3e22921401
14 changed files with 235 additions and 319 deletions

View File

@ -8,17 +8,17 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import {
Document,
Model,
Query,
Types,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { Model, Types } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import {
Args,
postDelete,
preCreate,
preDelete,
preUpdate,
preUpdateMany,
} from '@/utils/types/lifecycle-hook-manager.types';
import { BlockCreateDto, BlockDto, BlockUpdateDto } from '../dto/block.dto';
import {
@ -63,10 +63,8 @@ export class BlockRepository extends BaseRepository<
*
* @param doc - The document that is being created.
*/
async preCreate(
_doc: Document<unknown, object, Block> & Block & { _id: Types.ObjectId },
): Promise<void> {
if (_doc) this.checkDeprecatedAttachmentUrl(_doc);
async preCreate(...[doc]: Args<preCreate<Block>>): Promise<void> {
if (doc) this.checkDeprecatedAttachmentUrl(doc);
}
/**
@ -77,19 +75,9 @@ export class BlockRepository extends BaseRepository<
* @param updates - The update data.
*/
async preUpdate(
_query: Query<
Document<Block, any, any>,
Document<Block, any, any>,
unknown,
Block,
'findOneAndUpdate'
>,
criteria: TFilterQuery<Block>,
updates:
| UpdateWithAggregationPipeline
| UpdateQuery<Document<Block, any, any>>,
...[, criteria, updates]: Args<preUpdate<Block>>
): Promise<void> {
const update: BlockUpdateDto = updates?.['$set'];
const update = '$set' in updates ? updates.$set : {};
if (update?.category) {
const movedBlock = await this.findOne(criteria);
@ -109,7 +97,10 @@ export class BlockRepository extends BaseRepository<
{ $set: { attachedBlock: null } },
);
}
this.checkDeprecatedAttachmentUrl(update);
if (update) {
this.checkDeprecatedAttachmentUrl(update);
}
}
/**
@ -120,18 +111,10 @@ export class BlockRepository extends BaseRepository<
* @param updates - The update data.
*/
async preUpdateMany(
_query: Query<
Document<Block, any, any>,
Document<Block, any, any>,
unknown,
Block,
'updateMany',
Record<string, never>
>,
criteria: TFilterQuery<Block>,
updates: UpdateQuery<Document<Block, any, any>>,
...[, criteria, updates]: Args<preUpdateMany<Block>>
): Promise<void> {
const categoryId: string = updates.$set.category;
const categoryId = '$set' in updates && updates.$set?.category;
if (categoryId) {
const movedBlocks = await this.find(criteria);
@ -226,16 +209,7 @@ export class BlockRepository extends BaseRepository<
* @param query - The delete query.
* @param result - The result of the delete operation.
*/
async postDelete(
_query: Query<
DeleteResult,
Document<Block, any, any>,
unknown,
Block,
'deleteOne' | 'deleteMany'
>,
result: DeleteResult,
) {
async postDelete(...[, result]: Args<postDelete<Block>>) {
if (result.deletedCount > 0) {
}
}
@ -247,16 +221,7 @@ export class BlockRepository extends BaseRepository<
* @param query - The delete query.
* @param criteria - The filter criteria for finding blocks to delete.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<Block, any, any>,
unknown,
Block,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<Block>,
) {
async preDelete(...[, criteria]: Args<preDelete<Block>>) {
const docsToDelete = await this.model.find(criteria);
const idsToDelete = docsToDelete.map(({ id }) => id);
if (idsToDelete.length > 0) {

View File

@ -8,10 +8,10 @@
import { ForbiddenException, Injectable, Optional } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import { Args, preDelete } from '@/utils/types/lifecycle-hook-manager.types';
import { CategoryDto } from '../dto/category.dto';
import { Category } from '../schemas/category.schema';
@ -41,16 +41,7 @@ export class CategoryRepository extends BaseRepository<
* @param query - The delete query.
* @param criteria - The filter criteria for finding blocks to delete.
*/
async preDelete(
query: Query<
DeleteResult,
Document<Category, any, any>,
unknown,
Category,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<Category>,
) {
async preDelete(...[query, criteria]: Args<preDelete<Category>>) {
criteria = query.getQuery();
const ids = Array.isArray(criteria._id?.$in)
? criteria._id.$in

View File

@ -13,10 +13,10 @@ import {
Optional,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import { Args, preDelete } from '@/utils/types/lifecycle-hook-manager.types';
import { ContextVarDto } from '../dto/context-var.dto';
import { ContextVar } from '../schemas/context-var.schema';
@ -47,16 +47,7 @@ export class ContextVarRepository extends BaseRepository<
* @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>,
) {
async preDelete(...[, criteria]: Args<preDelete<ContextVar>>) {
const ids = Array.isArray(criteria._id) ? criteria._id : [criteria._id];
for (const id of ids) {

View File

@ -8,16 +8,19 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import {
Args,
postCreate,
preDelete,
} from '@/utils/types/lifecycle-hook-manager.types';
import { LabelDto } from '../dto/label.dto';
import {
Label,
LABEL_POPULATE,
LabelDocument,
LabelFull,
LabelPopulate,
} from '../schemas/label.schema';
@ -40,7 +43,7 @@ export class LabelRepository extends BaseRepository<
*
* @returns A promise that resolves when the update operation is complete.
*/
async postCreate(created: LabelDocument): Promise<void> {
async postCreate(...[created]: Args<postCreate<Label>>): Promise<void> {
this.eventEmitter.emit(
'hook:label:create',
created,
@ -68,16 +71,7 @@ export class LabelRepository extends BaseRepository<
*
* @returns {Promise<void>} A promise that resolves once the event is emitted.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<Label, any, any>,
unknown,
Label,
'deleteOne' | 'deleteMany'
>,
_criteria: TFilterQuery<Label>,
): Promise<void> {
async preDelete(...[, _criteria]: Args<preDelete<Label>>): Promise<void> {
const ids = Array.isArray(_criteria._id?.$in)
? _criteria._id.$in
: Array.isArray(_criteria._id)

View File

@ -8,23 +8,20 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import {
Document,
Model,
Query,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { Model } from 'mongoose';
import { BotStatsType } from '@/analytics/schemas/bot-stats.schema';
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import {
Args,
postCreate,
preUpdate,
} from '@/utils/types/lifecycle-hook-manager.types';
import { SubscriberDto, SubscriberUpdateDto } from '../dto/subscriber.dto';
import {
Subscriber,
SUBSCRIBER_POPULATE,
SubscriberDocument,
SubscriberFull,
SubscriberPopulate,
} from '../schemas/subscriber.schema';
@ -45,7 +42,7 @@ export class SubscriberRepository extends BaseRepository<
*
* @param created - The newly created subscriber document.
*/
async postCreate(created: SubscriberDocument): Promise<void> {
async postCreate(...[created]: Args<postCreate<Subscriber>>): Promise<void> {
this.eventEmitter.emit(
'hook:stats:entry',
BotStatsType.new_users,
@ -63,17 +60,7 @@ export class SubscriberRepository extends BaseRepository<
* @param updates - The update data, which may include fields like `assignedTo`.
*/
async preUpdate(
_query: Query<
Document<Subscriber, any, any>,
Document<Subscriber, any, any>,
unknown,
Subscriber,
'findOneAndUpdate'
>,
criteria: TFilterQuery<Subscriber>,
updates:
| UpdateWithAggregationPipeline
| UpdateQuery<Document<Subscriber, any, any>>,
...[, criteria, updates]: Args<preUpdate<Subscriber>>
): Promise<void> {
const subscriberUpdates: SubscriberUpdateDto = updates?.['$set'];

View File

@ -8,11 +8,11 @@
import { ForbiddenException, Injectable, Optional } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BlockService } from '@/chat/services/block.service';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import { Args, preDelete } from '@/utils/types/lifecycle-hook-manager.types';
import { ContentTypeDto } from '../dto/contentType.dto';
import { ContentType } from '../schemas/content-type.schema';
@ -42,25 +42,16 @@ export class ContentTypeRepository extends BaseRepository<
* @param query - The query object used for deletion.
* @param criteria - The filter query to identify the content type entity to delete.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<ContentType, any, any>,
unknown,
ContentType,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<ContentType>,
) {
const entityId: string = criteria._id as string;
async preDelete(...[, criteria]: Args<preDelete<ContentType>>) {
const entityId = criteria._id;
const associatedBlocks = await this.blockService?.findOne({
'options.content.entity': entityId,
});
if (associatedBlocks) {
throw new ForbiddenException(`Content type have blocks associated to it`);
}
if (criteria._id) {
await this.contentModel.deleteMany({ entity: criteria._id });
if (entityId) {
await this.contentModel.deleteMany({ entity: entityId });
} else {
throw new Error(
'Attempted to delete content type using unknown criteria',

View File

@ -8,17 +8,14 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import {
Document,
HydratedDocument,
Model,
Query,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import {
Args,
preCreate,
preUpdate,
} from '@/utils/types/lifecycle-hook-manager.types';
import { ContentDto } from '../dto/content.dto';
import {
@ -45,7 +42,7 @@ export class ContentRepository extends BaseRepository<
*
* @param doc - The document that is about to be created.
*/
async preCreate(_doc: HydratedDocument<Content>) {
async preCreate(...[_doc]: Args<preCreate<Content>>) {
_doc.set('rag', this.stringify(_doc.dynamicFields));
}
@ -58,17 +55,7 @@ export class ContentRepository extends BaseRepository<
* @param updates - The update operations to be applied to the document.
*/
async preUpdate(
_query: Query<
Document<Content, any, any>,
Document<Content, any, any>,
unknown,
Content,
'findOneAndUpdate'
>,
_criteria: TFilterQuery<Content>,
_updates:
| UpdateWithAggregationPipeline
| UpdateQuery<Document<Content, any, any>>,
...[_query, , _updates]: Args<preUpdate<Content>>
): Promise<void> {
if ('dynamicFields' in _updates['$set']) {
_query.set('rag', this.stringify(_updates['$set']['dynamicFields']));

View File

@ -8,16 +8,20 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import {
Args,
postCreate,
postUpdate,
preDelete,
} from '@/utils/types/lifecycle-hook-manager.types';
import { NlpEntityDto } from '../dto/nlp-entity.dto';
import {
NLP_ENTITY_POPULATE,
NlpEntity,
NlpEntityDocument,
NlpEntityFull,
NlpEntityPopulate,
} from '../schemas/nlp-entity.schema';
@ -47,7 +51,7 @@ export class NlpEntityRepository extends BaseRepository<
*
* @param created - The newly created NLP entity document.
*/
async postCreate(_created: NlpEntityDocument): Promise<void> {
async postCreate(...[_created]: Args<postCreate<NlpEntity>>): Promise<void> {
if (!_created.builtin) {
// Bypass builtin entities (probably fixtures)
this.eventEmitter.emit('hook:nlpEntity:create', _created);
@ -62,16 +66,7 @@ export class NlpEntityRepository extends BaseRepository<
* @param query - The query used to find and update the entity.
* @param updated - The updated NLP entity document.
*/
async postUpdate(
_query: Query<
Document<NlpEntity, any, any>,
Document<NlpEntity, any, any>,
unknown,
NlpEntity,
'findOneAndUpdate'
>,
updated: NlpEntity,
): Promise<void> {
async postUpdate(...[, updated]: Args<postUpdate<NlpEntity>>): Promise<void> {
if (!updated?.builtin) {
// Bypass builtin entities (probably fixtures)
this.eventEmitter.emit('hook:nlpEntity:update', updated);
@ -87,16 +82,7 @@ export class NlpEntityRepository extends BaseRepository<
* @param query The query used to delete the entity.
* @param criteria The filter criteria used to find the entity for deletion.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<NlpEntity, any, any>,
unknown,
NlpEntity,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<NlpEntity>,
): Promise<void> {
async preDelete(...[, criteria]: Args<preDelete<NlpEntity>>): Promise<void> {
if (criteria._id) {
await this.nlpValueRepository.deleteMany({ entity: criteria._id });
await this.nlpSampleEntityRepository.deleteMany({ entity: criteria._id });

View File

@ -8,10 +8,10 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, Query } from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BaseRepository } from '@/utils/generics/base-repository';
import { Args, preDelete } from '@/utils/types/lifecycle-hook-manager.types';
import { TNlpSampleDto } from '../dto/nlp-sample.dto';
import {
@ -43,16 +43,7 @@ export class NlpSampleRepository extends BaseRepository<
* @param query - The query object used for deletion.
* @param criteria - Criteria to identify the sample(s) to delete.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<NlpSample, any, any>,
unknown,
NlpSample,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<NlpSample>,
) {
async preDelete(...[, criteria]: Args<preDelete<NlpSample>>) {
if (criteria._id) {
await this.nlpSampleEntityRepository.deleteMany({
sample: criteria._id,

View File

@ -9,25 +9,23 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { plainToInstance } from 'class-transformer';
import {
Document,
Model,
PipelineStage,
Query,
SortOrder,
Types,
} from 'mongoose';
import { Model, PipelineStage, SortOrder, Types } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { BaseRepository } from '@/utils/generics/base-repository';
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
import { TFilterQuery } from '@/utils/types/filter.types';
import { Format } from '@/utils/types/format.types';
import {
Args,
postCreate,
postUpdate,
preDelete,
} from '@/utils/types/lifecycle-hook-manager.types';
import { NlpValueDto } from '../dto/nlp-value.dto';
import {
NLP_VALUE_POPULATE,
NlpValue,
NlpValueDocument,
NlpValueFull,
NlpValueFullWithCount,
NlpValuePopulate,
@ -56,7 +54,7 @@ export class NlpValueRepository extends BaseRepository<
*
* @param created - The newly created NLP value document.
*/
async postCreate(created: NlpValueDocument): Promise<void> {
async postCreate(...[created]: Args<postCreate<NlpValue>>): Promise<void> {
if (!created.builtin) {
// Bypass builtin entities (probably fixtures)
this.eventEmitter.emit('hook:nlpValue:create', created);
@ -69,16 +67,7 @@ export class NlpValueRepository extends BaseRepository<
* @param query - The query that was used to update the NLP value.
* @param updated - The updated NLP value document.
*/
async postUpdate(
_query: Query<
Document<NlpValue, any, any>,
Document<NlpValue, any, any>,
unknown,
NlpValue,
'findOneAndUpdate'
>,
updated: NlpValue,
): Promise<void> {
async postUpdate(...[, updated]: Args<postUpdate<NlpValue>>): Promise<void> {
if (!updated?.builtin) {
// Bypass builtin entities (probably fixtures)
this.eventEmitter.emit('hook:nlpValue:update', updated);
@ -92,16 +81,7 @@ export class NlpValueRepository extends BaseRepository<
* @param _query - The query used to delete the NLP value(s).
* @param criteria - The filter criteria used to identify the NLP value(s) to delete.
*/
async preDelete(
_query: Query<
DeleteResult,
Document<NlpValue, any, any>,
unknown,
NlpValue,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<NlpValue>,
): Promise<void> {
async preDelete(...[, criteria]: Args<preDelete<NlpValue>>): Promise<void> {
if (criteria._id) {
await this.nlpSampleEntityRepository.deleteMany({ value: criteria._id });

View File

@ -9,17 +9,15 @@
import { Injectable } from '@nestjs/common';
import { IHookSettingsGroupLabelOperationMap } from '@nestjs/event-emitter';
import { InjectModel } from '@nestjs/mongoose';
import {
Document,
FilterQuery,
Model,
Query,
Types,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import {
Args,
postUpdate,
preCreateValidate,
preUpdateValidate,
} from '@/utils/types/lifecycle-hook-manager.types';
import { Setting } from '../schemas/setting.schema';
import { SettingType } from '../schemas/types';
@ -30,16 +28,12 @@ export class SettingRepository extends BaseRepository<Setting> {
super(model, Setting);
}
async preCreateValidate(
doc: Document<unknown, unknown, Setting> &
Setting & { _id: Types.ObjectId },
) {
async preCreateValidate(...[doc]: Args<preCreateValidate<Setting>>) {
this.validateSettingValue(doc.type, doc.value);
}
async preUpdateValidate(
criteria: FilterQuery<Setting>,
updates: UpdateWithAggregationPipeline | UpdateQuery<Setting>,
...[criteria, updates]: Args<preUpdateValidate<Setting>>
): Promise<void> {
if (!Array.isArray(updates)) {
const payload = updates.$set;
@ -70,21 +64,12 @@ export class SettingRepository extends BaseRepository<Setting> {
* @param _query The Mongoose query object used to find and update the document.
* @param setting The updated `Setting` object.
*/
async postUpdate(
_query: Query<
Document<Setting, any, any>,
Document<Setting, any, any>,
unknown,
Setting,
'findOneAndUpdate'
>,
setting: Setting,
) {
const group = setting.group as keyof IHookSettingsGroupLabelOperationMap;
const label = setting.label as '*';
async postUpdate(...[, updated]: Args<postUpdate<Setting>>) {
const group = updated.group as keyof IHookSettingsGroupLabelOperationMap;
const label = updated.label as '*';
// Sync global settings var
this.eventEmitter.emit(`hook:${group}:${label}`, setting);
this.eventEmitter.emit(`hook:${group}:${label}`, updated);
}
/**

View File

@ -8,22 +8,19 @@
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import {
Document,
Model,
Query,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import {
Args,
preCreate,
preUpdate,
} from '@/utils/types/lifecycle-hook-manager.types';
import { UserDto, UserEditProfileDto } from '../dto/user.dto';
import {
User,
USER_POPULATE,
UserDocument,
UserFull,
UserPopulate,
} from '../schemas/user.schema';
@ -46,7 +43,7 @@ export class UserRepository extends BaseRepository<
*
* @param _doc The user document being created.
*/
async preCreate(_doc: UserDocument) {
async preCreate(...[_doc]: Args<preCreate<User>>) {
if (_doc?.password) {
_doc.password = hash(_doc.password);
} else {
@ -62,19 +59,7 @@ export class UserRepository extends BaseRepository<
* @param _criteria The criteria used to filter the user documents to update.
* @param _updates The update object that may contain password or resetToken to be hashed.
*/
async preUpdate(
_query: Query<
Document<User, any, any>,
Document<User, any, any>,
unknown,
User,
'findOneAndUpdate'
>,
_criteria: TFilterQuery<User>,
_updates:
| UpdateWithAggregationPipeline
| UpdateQuery<Document<User, any, any>>,
) {
async preUpdate(...[_query, , _updates]: Args<preUpdate<User>>) {
const updates: UserEditProfileDto & {
resetToken?: string;
} = _updates?.['$set'];

View File

@ -15,7 +15,6 @@ import {
import { ClassTransformOptions, plainToClass } from 'class-transformer';
import {
Document,
FilterQuery,
FlattenMaps,
HydratedDocument,
Model,
@ -23,7 +22,6 @@ import {
Query,
SortOrder,
UpdateQuery,
UpdateWithAggregationPipeline,
UpdateWriteOpResult,
} from 'mongoose';
@ -37,6 +35,22 @@ import {
import { flatten } from '../helpers/flatten';
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import {
Args,
postCreate,
postCreateValidate,
postDelete,
postUpdate,
postUpdateMany,
postUpdateValidate,
preCreate,
preCreateValidate,
preDelete,
preUpdate,
preUpdateMany,
preUpdateValidate,
R,
} from '../types/lifecycle-hook-manager.types';
import { BaseSchema } from './base-schema';
import { LifecycleHookManager } from './lifecycle-hook-manager';
@ -79,7 +93,7 @@ export abstract class BaseRepository<
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
D = Document<T>,
D extends Document<T> = Document<T>,
> {
private readonly transformOpts = { excludePrefixes: ['_', 'password'] };
@ -567,80 +581,58 @@ export abstract class BaseRepository<
}
async preCreateValidate(
_doc: HydratedDocument<T>,
_filterCriteria?: FilterQuery<T>,
_updates?: UpdateWithAggregationPipeline | UpdateQuery<T>,
): Promise<void> {
..._: Args<preCreateValidate<T>>
): R<preCreateValidate<T>> {
// Nothing ...
}
async postCreateValidate(_validated: HydratedDocument<T>): Promise<void> {
async postCreateValidate(
..._: Args<postCreateValidate<T>>
): R<postCreateValidate<T>> {
// Nothing ...
}
async preUpdateValidate(
_filterCriteria: FilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
): Promise<void> {
..._: Args<preUpdateValidate<T>>
): R<preUpdateValidate<T>> {
// Nothing ...
}
async postUpdateValidate(
_filterCriteria: FilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
): Promise<void> {
..._: Args<postUpdateValidate<T>>
): R<postUpdateValidate<T>> {
// Nothing ...
}
async preCreate(_doc: HydratedDocument<T>): Promise<void> {
async preCreate(..._: Args<preCreate<T>>): R<postCreate<T>> {
// Nothing ...
}
async postCreate(_created: HydratedDocument<T>): Promise<void> {
async postCreate(..._: Args<postCreate<T>>): R<postCreate<T>> {
// Nothing ...
}
async preUpdate(
_query: Query<D, D, unknown, T, 'findOneAndUpdate'>,
_criteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<D>,
): Promise<void> {
async preUpdate(..._: Args<preUpdate<T>>): R<preUpdate<T>> {
// Nothing ...
}
async preUpdateMany(
_query: Query<D, D, unknown, T, 'updateMany'>,
_criteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<D>,
): Promise<void> {
async preUpdateMany(..._: Args<preUpdateMany<T>>): R<preUpdateMany<T>> {
// Nothing ...
}
async postUpdateMany(
_query: Query<D, D, unknown, T, 'updateMany'>,
_updated: any,
): Promise<void> {
async postUpdateMany(..._: Args<postUpdateMany<T>>): R<postUpdateMany<T>> {
// Nothing ...
}
async postUpdate(
_query: Query<D, D, unknown, T, 'findOneAndUpdate'>,
_updated: T,
): Promise<void> {
async postUpdate(..._: Args<postUpdate<T>>): R<preDelete<T>> {
// Nothing ...
}
async preDelete(
_query: Query<DeleteResult, D, unknown, T, 'deleteOne' | 'deleteMany'>,
_criteria: TFilterQuery<T>,
): Promise<void> {
async preDelete(..._: Args<preDelete<T>>): R<preDelete<T>> {
// Nothing ...
}
async postDelete(
_query: Query<DeleteResult, D, unknown, T, 'deleteOne' | 'deleteMany'>,
_result: DeleteResult,
): Promise<void> {
async postDelete(..._: Args<postDelete<T>>): R<postDelete<T>> {
// Nothing ...
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 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 {
Document,
HydratedDocument,
Query,
UpdateQuery,
UpdateWithAggregationPipeline,
} from 'mongoose';
import { DeleteResult } from '../generics/base-repository';
import { TFilterQuery } from './filter.types';
export type Args<T extends (...args: any) => unknown> = Parameters<T>;
export type R<T extends (...args: any) => unknown> = ReturnType<T>;
export type preCreateValidate<T> = (
_doc: HydratedDocument<T>,
_filterCriteria?: TFilterQuery<T>,
_updates?: UpdateWithAggregationPipeline | UpdateQuery<T>,
) => Promise<void>;
export type postCreateValidate<T> = (
_validated: HydratedDocument<T>,
) => Promise<void>;
export type preUpdateValidate<T> = (
_filterCriteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
) => Promise<void>;
export type postUpdateValidate<T> = (
_filterCriteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<T>,
) => Promise<void>;
export type preCreate<T> = (_doc: HydratedDocument<T>) => Promise<void>;
export type postCreate<T> = (_created: HydratedDocument<T>) => Promise<void>;
export type preUpdate<T> = (
_query: Query<Document<T>, Document<T>, unknown, T, 'findOneAndUpdate'>,
_criteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<Document<T>>,
) => Promise<void>;
export type preUpdateMany<T> = (
_query: Query<Document<T>, Document<T>, unknown, T, 'updateMany'>,
_criteria: TFilterQuery<T>,
_updates: UpdateWithAggregationPipeline | UpdateQuery<Document<T>>,
) => Promise<void>;
export type postUpdateMany<T> = (
_query: Query<Document<T>, Document<T>, unknown, T, 'updateMany'>,
_updated: any,
) => Promise<void>;
export type postUpdate<T> = (
_query: Query<Document<T>, Document<T>, unknown, T, 'findOneAndUpdate'>,
_updated: T,
) => Promise<void>;
export type preDelete<T> = (
_query: Query<
DeleteResult,
Document<T>,
unknown,
T,
'deleteOne' | 'deleteMany'
>,
_criteria: TFilterQuery<T>,
) => Promise<void>;
export type postDelete<T> = (
_query: Query<
DeleteResult,
Document<T>,
unknown,
T,
'deleteOne' | 'deleteMany'
>,
_result: DeleteResult,
) => Promise<void>;