mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
Merge pull request #57 from Hexastack/refactor/api-populate-queries
refactor: populate queries
This commit is contained in:
commit
86673b3b59
@ -16,7 +16,7 @@ import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { BotStats, BotStatsType } from '../schemas/bot-stats.schema';
|
||||
|
||||
@Injectable()
|
||||
export class BotStatsRepository extends BaseRepository<BotStats, never> {
|
||||
export class BotStatsRepository extends BaseRepository<BotStats> {
|
||||
constructor(@InjectModel(BotStats.name) readonly model: Model<BotStats>) {
|
||||
super(model, BotStats);
|
||||
}
|
||||
|
||||
@ -36,14 +36,24 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { BlockCreateDto, BlockUpdateDto } from '../dto/block.dto';
|
||||
import { Block, BlockFull, BlockStub } from '../schemas/block.schema';
|
||||
import {
|
||||
Block,
|
||||
BlockFull,
|
||||
BlockPopulate,
|
||||
BlockStub,
|
||||
} from '../schemas/block.schema';
|
||||
import { BlockService } from '../services/block.service';
|
||||
import { CategoryService } from '../services/category.service';
|
||||
import { LabelService } from '../services/label.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('Block')
|
||||
export class BlockController extends BaseController<Block, BlockStub> {
|
||||
export class BlockController extends BaseController<
|
||||
Block,
|
||||
BlockStub,
|
||||
BlockPopulate,
|
||||
BlockFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly blockService: BlockService,
|
||||
private readonly logger: LoggerService,
|
||||
@ -68,15 +78,7 @@ export class BlockController extends BaseController<Block, BlockStub> {
|
||||
@Query(new SearchFilterPipe<Block>({ allowedFields: ['category'] }))
|
||||
filters: TFilterQuery<Block>,
|
||||
): Promise<Block[] | BlockFull[]> {
|
||||
return this.canPopulate(populate, [
|
||||
'trigger_labels',
|
||||
'assign_labels',
|
||||
'nextBlocks',
|
||||
'attachedBlock',
|
||||
'category',
|
||||
'previousBlocks',
|
||||
'attachedToBlock',
|
||||
])
|
||||
return this.canPopulate(populate)
|
||||
? await this.blockService.findAndPopulate(filters)
|
||||
: await this.blockService.find(filters);
|
||||
}
|
||||
@ -189,15 +191,7 @@ export class BlockController extends BaseController<Block, BlockStub> {
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
): Promise<Block | BlockFull> {
|
||||
const doc = this.canPopulate(populate, [
|
||||
'trigger_labels',
|
||||
'assign_labels',
|
||||
'nextBlocks',
|
||||
'attachedBlock',
|
||||
'category',
|
||||
'previousBlocks',
|
||||
'attachedToBlock',
|
||||
])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.blockService.findOneAndPopulate(id)
|
||||
: await this.blockService.findOne(id);
|
||||
if (!doc) {
|
||||
|
||||
@ -33,7 +33,6 @@ import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { CategoryCreateDto, CategoryUpdateDto } from '../dto/category.dto';
|
||||
import { Category } from '../schemas/category.schema';
|
||||
import { BlockService } from '../services/block.service';
|
||||
import { CategoryService } from '../services/category.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@ -41,7 +40,6 @@ import { CategoryService } from '../services/category.service';
|
||||
export class CategoryController extends BaseController<Category> {
|
||||
constructor(
|
||||
private readonly categoryService: CategoryService,
|
||||
private readonly blockService: BlockService,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(categoryService);
|
||||
|
||||
@ -32,12 +32,22 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { LabelCreateDto, LabelUpdateDto } from '../dto/label.dto';
|
||||
import { Label, LabelStub } from '../schemas/label.schema';
|
||||
import {
|
||||
Label,
|
||||
LabelFull,
|
||||
LabelPopulate,
|
||||
LabelStub,
|
||||
} from '../schemas/label.schema';
|
||||
import { LabelService } from '../services/label.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('label')
|
||||
export class LabelController extends BaseController<Label, LabelStub> {
|
||||
export class LabelController extends BaseController<
|
||||
Label,
|
||||
LabelStub,
|
||||
LabelPopulate,
|
||||
LabelFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly labelService: LabelService,
|
||||
private readonly logger: LoggerService,
|
||||
@ -53,7 +63,7 @@ export class LabelController extends BaseController<Label, LabelStub> {
|
||||
@Query(new SearchFilterPipe<Label>({ allowedFields: ['name', 'title'] }))
|
||||
filters: TFilterQuery<Label>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['users'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.labelService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.labelService.findPage(filters, pageQuery);
|
||||
}
|
||||
@ -80,7 +90,7 @@ export class LabelController extends BaseController<Label, LabelStub> {
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['users'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.labelService.findOneAndPopulate(id)
|
||||
: await this.labelService.findOne(id);
|
||||
if (!doc) {
|
||||
|
||||
@ -36,7 +36,12 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { MessageCreateDto } from '../dto/message.dto';
|
||||
import { Message, MessageStub } from '../schemas/message.schema';
|
||||
import {
|
||||
Message,
|
||||
MessageFull,
|
||||
MessagePopulate,
|
||||
MessageStub,
|
||||
} from '../schemas/message.schema';
|
||||
import {
|
||||
OutgoingMessage,
|
||||
OutgoingMessageFormat,
|
||||
@ -49,7 +54,12 @@ import { SubscriberService } from '../services/subscriber.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('message')
|
||||
export class MessageController extends BaseController<Message, MessageStub> {
|
||||
export class MessageController extends BaseController<
|
||||
Message,
|
||||
MessageStub,
|
||||
MessagePopulate,
|
||||
MessageFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly messageService: MessageService,
|
||||
private readonly subscriberService: SubscriberService,
|
||||
@ -70,7 +80,7 @@ export class MessageController extends BaseController<Message, MessageStub> {
|
||||
)
|
||||
filters: TFilterQuery<Message>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['recipient', 'sender', 'sentBy'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.messageService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.messageService.findPage(filters, pageQuery);
|
||||
}
|
||||
@ -97,7 +107,7 @@ export class MessageController extends BaseController<Message, MessageStub> {
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['recipient', 'sender', 'sentBy'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.messageService.findOneAndPopulate(id)
|
||||
: await this.messageService.findOne(id);
|
||||
if (!doc) {
|
||||
|
||||
@ -32,14 +32,21 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { SubscriberUpdateDto } from '../dto/subscriber.dto';
|
||||
import { Subscriber, SubscriberStub } from '../schemas/subscriber.schema';
|
||||
import {
|
||||
Subscriber,
|
||||
SubscriberFull,
|
||||
SubscriberPopulate,
|
||||
SubscriberStub,
|
||||
} from '../schemas/subscriber.schema';
|
||||
import { SubscriberService } from '../services/subscriber.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('subscriber')
|
||||
export class SubscriberController extends BaseController<
|
||||
Subscriber,
|
||||
SubscriberStub
|
||||
SubscriberStub,
|
||||
SubscriberPopulate,
|
||||
SubscriberFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly subscriberService: SubscriberService,
|
||||
@ -67,7 +74,7 @@ export class SubscriberController extends BaseController<
|
||||
)
|
||||
filters: TFilterQuery<Subscriber>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['labels', 'assignedTo', 'avatar'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.subscriberService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.subscriberService.findPage(filters, pageQuery);
|
||||
}
|
||||
@ -100,7 +107,7 @@ export class SubscriberController extends BaseController<
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['labels', 'assignedTo', 'avatar'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.subscriberService.findOneAndPopulate(id)
|
||||
: await this.subscriberService.findOne(id);
|
||||
if (!doc) {
|
||||
|
||||
@ -10,11 +10,11 @@
|
||||
import { Injectable, Optional } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import {
|
||||
TFilterQuery,
|
||||
Model,
|
||||
Document,
|
||||
Types,
|
||||
Model,
|
||||
Query,
|
||||
TFilterQuery,
|
||||
Types,
|
||||
UpdateQuery,
|
||||
UpdateWithAggregationPipeline,
|
||||
} from 'mongoose';
|
||||
@ -23,27 +23,24 @@ import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { BlockCreateDto, BlockUpdateDto } from '../dto/block.dto';
|
||||
import { Block, BlockFull } from '../schemas/block.schema';
|
||||
import {
|
||||
Block,
|
||||
BLOCK_POPULATE,
|
||||
BlockFull,
|
||||
BlockPopulate,
|
||||
} from '../schemas/block.schema';
|
||||
|
||||
@Injectable()
|
||||
export class BlockRepository extends BaseRepository<
|
||||
Block,
|
||||
| 'trigger_labels'
|
||||
| 'assign_labels'
|
||||
| 'nextBlocks'
|
||||
| 'attachedBlock'
|
||||
| 'category'
|
||||
| 'previousBlocks'
|
||||
| 'attachedToBlock'
|
||||
BlockPopulate,
|
||||
BlockFull
|
||||
> {
|
||||
private readonly logger: LoggerService;
|
||||
|
||||
constructor(
|
||||
@InjectModel(Block.name) readonly model: Model<Block>,
|
||||
@Optional() logger?: LoggerService,
|
||||
@Optional() private readonly logger?: LoggerService,
|
||||
) {
|
||||
super(model, Block);
|
||||
this.logger = logger;
|
||||
super(model, Block, BLOCK_POPULATE, BlockFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,44 +157,4 @@ export class BlockRepository extends BaseRepository<
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds blocks and populates related fields (e.g., labels, attached blocks).
|
||||
*
|
||||
* @param filters - The filter criteria for finding blocks.
|
||||
*
|
||||
* @returns The populated block results.
|
||||
*/
|
||||
async findAndPopulate(filters: TFilterQuery<Block>) {
|
||||
const query = this.findQuery(filters).populate([
|
||||
'trigger_labels',
|
||||
'assign_labels',
|
||||
'nextBlocks',
|
||||
'attachedBlock',
|
||||
'category',
|
||||
'previousBlocks',
|
||||
'attachedToBlock',
|
||||
]);
|
||||
return await this.execute(query, BlockFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single block by ID and populates related fields (e.g., labels, attached blocks).
|
||||
*
|
||||
* @param id - The ID of the block to find.
|
||||
*
|
||||
* @returns The populated block result or null if not found.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate([
|
||||
'trigger_labels',
|
||||
'assign_labels',
|
||||
'nextBlocks',
|
||||
'attachedBlock',
|
||||
'category',
|
||||
'previousBlocks',
|
||||
'attachedToBlock',
|
||||
]);
|
||||
return await this.executeOne(query, BlockFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import { ForbiddenException, Injectable, Optional } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Category } from '../schemas/category.schema';
|
||||
@ -19,17 +18,13 @@ import { BlockService } from '../services/block.service';
|
||||
|
||||
@Injectable()
|
||||
export class CategoryRepository extends BaseRepository<Category> {
|
||||
private readonly logger: LoggerService;
|
||||
|
||||
private readonly blockService: BlockService;
|
||||
|
||||
constructor(
|
||||
@InjectModel(Category.name) readonly model: Model<Category>,
|
||||
@Optional() blockService?: BlockService,
|
||||
@Optional() logger?: LoggerService,
|
||||
) {
|
||||
super(model, Category);
|
||||
this.logger = logger;
|
||||
this.blockService = blockService;
|
||||
}
|
||||
|
||||
|
||||
@ -16,20 +16,23 @@ import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
|
||||
import {
|
||||
Conversation,
|
||||
CONVERSATION_POPULATE,
|
||||
ConversationDocument,
|
||||
ConversationFull,
|
||||
ConversationPopulate,
|
||||
} from '../schemas/conversation.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ConversationRepository extends BaseRepository<
|
||||
Conversation,
|
||||
'sender' | 'current' | 'next'
|
||||
ConversationPopulate,
|
||||
ConversationFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Conversation.name) readonly model: Model<Conversation>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Conversation);
|
||||
super(model, Conversation, CONVERSATION_POPULATE, ConversationFull);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -10,22 +10,29 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { TFilterQuery, Model, Document, Query } from 'mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { Label, LabelDocument, LabelFull } from '../schemas/label.schema';
|
||||
import {
|
||||
Label,
|
||||
LABEL_POPULATE,
|
||||
LabelDocument,
|
||||
LabelFull,
|
||||
LabelPopulate,
|
||||
} from '../schemas/label.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LabelRepository extends BaseRepository<Label, 'users'> {
|
||||
export class LabelRepository extends BaseRepository<
|
||||
Label,
|
||||
LabelPopulate,
|
||||
LabelFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Label.name) readonly model: Model<Label>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(model, Label);
|
||||
super(model, Label, LABEL_POPULATE, LabelFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,42 +85,4 @@ export class LabelRepository extends BaseRepository<Label, 'users'> {
|
||||
);
|
||||
this.eventEmitter.emit('hook:chatbot:label:delete', labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all label documents and populates the `users` field which references the subscribers.
|
||||
*
|
||||
* @returns A promise that resolves with an array of fully populated `LabelFull` documents.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
const query = this.findAllQuery().populate(['users']);
|
||||
return await this.execute(query, LabelFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a paginated list of label documents based on filters and populates the `users` (subscribers) field.
|
||||
*
|
||||
* @param filters - The filter criteria for querying the labels.
|
||||
* @param pageQuery - The pagination query options.
|
||||
*
|
||||
* @returns A promise that resolves with a paginated array of fully populated `LabelFull` documents.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<Label>,
|
||||
pageQuery: PageQueryDto<Label>,
|
||||
) {
|
||||
const query = this.findPageQuery(filters, pageQuery).populate(['users']);
|
||||
return await this.execute(query, LabelFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a single label document by its ID and populates the `users` (subscribers) field.
|
||||
*
|
||||
* @param id - The ID of the label to be fetched.
|
||||
*
|
||||
* @returns A promise that resolves with a fully populated label.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate(['users']);
|
||||
return await this.executeOne(query, LabelFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,23 +9,28 @@
|
||||
|
||||
import { Injectable, Optional } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { TFilterQuery, Model, Query } from 'mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { NlpSampleCreateDto } from '@/nlp/dto/nlp-sample.dto';
|
||||
import { NlpSampleState } from '@/nlp/schemas/types';
|
||||
import { NlpSampleService } from '@/nlp/services/nlp-sample.service';
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { Message, MessageFull } from '../schemas/message.schema';
|
||||
import {
|
||||
Message,
|
||||
MESSAGE_POPULATE,
|
||||
MessageFull,
|
||||
MessagePopulate,
|
||||
} from '../schemas/message.schema';
|
||||
import { Subscriber } from '../schemas/subscriber.schema';
|
||||
import { AnyMessage } from '../schemas/types/message';
|
||||
|
||||
@Injectable()
|
||||
export class MessageRepository extends BaseRepository<
|
||||
AnyMessage,
|
||||
'sender' | 'recipient'
|
||||
MessagePopulate,
|
||||
MessageFull
|
||||
> {
|
||||
private readonly nlpSampleService: NlpSampleService;
|
||||
|
||||
@ -36,7 +41,12 @@ export class MessageRepository extends BaseRepository<
|
||||
@Optional() nlpSampleService?: NlpSampleService,
|
||||
@Optional() logger?: LoggerService,
|
||||
) {
|
||||
super(model, Message as new () => AnyMessage);
|
||||
super(
|
||||
model,
|
||||
Message as new () => AnyMessage,
|
||||
MESSAGE_POPULATE,
|
||||
MessageFull,
|
||||
);
|
||||
this.logger = logger;
|
||||
this.nlpSampleService = nlpSampleService;
|
||||
}
|
||||
@ -81,42 +91,6 @@ export class MessageRepository extends BaseRepository<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of messages with sender and recipient populated.
|
||||
* Uses filter criteria and pagination settings for the query.
|
||||
*
|
||||
* @param filters - Filter criteria for querying messages.
|
||||
* @param pageQuery - Pagination settings, including skip, limit, and sort order.
|
||||
*
|
||||
* @returns A paginated list of messages with sender and recipient details populated.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<AnyMessage>,
|
||||
pageQuery: PageQueryDto<AnyMessage>,
|
||||
) {
|
||||
const query = this.findPageQuery(filters, pageQuery).populate([
|
||||
'sender',
|
||||
'recipient',
|
||||
]);
|
||||
|
||||
return await this.execute(
|
||||
query as Query<AnyMessage[], AnyMessage, object, AnyMessage, 'find'>,
|
||||
MessageFull,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single message by its ID, populating the sender and recipient fields.
|
||||
*
|
||||
* @param id - The ID of the message to retrieve.
|
||||
*
|
||||
* @returns The message with sender and recipient details populated.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate(['sender', 'recipient']);
|
||||
return await this.executeOne(query, MessageFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the message history for a given subscriber, with messages sent or received
|
||||
* before the specified date. Results are limited and sorted by creation date.
|
||||
|
||||
@ -20,25 +20,27 @@ import {
|
||||
} from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { SubscriberUpdateDto } from '../dto/subscriber.dto';
|
||||
import {
|
||||
Subscriber,
|
||||
SUBSCRIBER_POPULATE,
|
||||
SubscriberDocument,
|
||||
SubscriberFull,
|
||||
SubscriberPopulate,
|
||||
} from '../schemas/subscriber.schema';
|
||||
|
||||
@Injectable()
|
||||
export class SubscriberRepository extends BaseRepository<
|
||||
Subscriber,
|
||||
'labels' | 'assignedTo' | 'avatar'
|
||||
SubscriberPopulate,
|
||||
SubscriberFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Subscriber.name) readonly model: Model<Subscriber>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Subscriber);
|
||||
super(model, Subscriber, SUBSCRIBER_POPULATE, SubscriberFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,11 +161,8 @@ export class SubscriberRepository extends BaseRepository<
|
||||
* @returns The found subscriber entity with populated fields.
|
||||
*/
|
||||
async findOneByForeignIdAndPopulate(id: string): Promise<SubscriberFull> {
|
||||
const query = this.findByForeignIdQuery(id).populate([
|
||||
'labels',
|
||||
'assignedTo',
|
||||
]);
|
||||
const [result] = await this.execute(query, SubscriberFull);
|
||||
const query = this.findByForeignIdQuery(id).populate(this.populate);
|
||||
const [result] = await this.execute(query, this.clsPopulate);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -223,56 +222,4 @@ export class SubscriberRepository extends BaseRepository<
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all subscribers and populates related fields such as `labels`, `assignedTo`, and `avatar`.
|
||||
*
|
||||
* @returns A list of all subscribers with populated fields.
|
||||
*/
|
||||
async findAllAndPopulate(): Promise<SubscriberFull[]> {
|
||||
const query = this.findAllQuery().populate([
|
||||
'labels',
|
||||
'assignedTo',
|
||||
'avatar',
|
||||
]);
|
||||
return await this.execute(query, SubscriberFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds subscribers using pagination and populates related fields such as `labels`, `assignedTo`, and `avatar`.
|
||||
*
|
||||
* @param filters - The filter criteria to apply when finding subscribers.
|
||||
* @param pageQuery - The pagination query.
|
||||
*
|
||||
* @returns A paginated list of subscribers with populated fields.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<Subscriber>,
|
||||
pageQuery: PageQueryDto<Subscriber>,
|
||||
): Promise<SubscriberFull[]> {
|
||||
const query = this.findPageQuery(filters, pageQuery).populate([
|
||||
'labels',
|
||||
'assignedTo',
|
||||
'avatar',
|
||||
]);
|
||||
return await this.execute(query, SubscriberFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single subscriber by criteria and populates related fields such as `labels`, `assignedTo`, and `avatar`.
|
||||
*
|
||||
* @param criteria - The filter criteria to apply when finding a subscriber.
|
||||
*
|
||||
* @returns The found subscriber entity with populated fields.
|
||||
*/
|
||||
async findOneAndPopulate(
|
||||
criteria: string | TFilterQuery<Subscriber>,
|
||||
): Promise<SubscriberFull> {
|
||||
const query = this.findOneQuery(criteria).populate([
|
||||
'labels',
|
||||
'assignedTo',
|
||||
'avatar',
|
||||
]);
|
||||
return await this.executeOne(query, SubscriberFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import { Schema as MongooseSchema, THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Category } from './category.schema';
|
||||
import { Label } from './label.schema';
|
||||
@ -194,3 +195,15 @@ BlockModel.schema.virtual('attachedToBlock', {
|
||||
});
|
||||
|
||||
export default BlockModel.schema;
|
||||
|
||||
export type BlockPopulate = keyof TFilterPopulateFields<Block, BlockStub>;
|
||||
|
||||
export const BLOCK_POPULATE: BlockPopulate[] = [
|
||||
'trigger_labels',
|
||||
'assign_labels',
|
||||
'nextBlocks',
|
||||
'attachedBlock',
|
||||
'category',
|
||||
'previousBlocks',
|
||||
'attachedToBlock',
|
||||
];
|
||||
|
||||
@ -12,6 +12,7 @@ import { Transform, Type } from 'class-transformer';
|
||||
import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Block } from './block.schema';
|
||||
import { Subscriber } from './subscriber.schema';
|
||||
@ -103,3 +104,14 @@ export const ConversationModel: ModelDefinition = {
|
||||
};
|
||||
|
||||
export default ConversationModel.schema;
|
||||
|
||||
export type ConversationPopulate = keyof TFilterPopulateFields<
|
||||
Conversation,
|
||||
ConversationStub
|
||||
>;
|
||||
|
||||
export const CONVERSATION_POPULATE: ConversationPopulate[] = [
|
||||
'sender',
|
||||
'current',
|
||||
'next',
|
||||
];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Subscriber } from './subscriber.schema';
|
||||
|
||||
@ -77,3 +78,7 @@ LabelModel.schema.virtual('users', {
|
||||
});
|
||||
|
||||
export default LabelModel.schema;
|
||||
|
||||
export type LabelPopulate = keyof TFilterPopulateFields<Label, LabelStub>;
|
||||
|
||||
export const LABEL_POPULATE: LabelPopulate[] = ['users'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Subscriber } from './subscriber.schema';
|
||||
import { StdIncomingMessage, StdOutgoingMessage } from './types/message';
|
||||
@ -102,3 +103,7 @@ export const MessageModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default MessageModel.schema;
|
||||
|
||||
export type MessagePopulate = keyof TFilterPopulateFields<Message, MessageStub>;
|
||||
|
||||
export const MESSAGE_POPULATE: MessagePopulate[] = ['sender', 'recipient'];
|
||||
|
||||
@ -15,6 +15,7 @@ import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||
import { User } from '@/user/schemas/user.schema';
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Label } from './label.schema';
|
||||
import { ChannelData } from './types/channel';
|
||||
@ -140,3 +141,14 @@ export const SubscriberModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default SubscriberModel.schema;
|
||||
|
||||
export type SubscriberPopulate = keyof TFilterPopulateFields<
|
||||
Subscriber,
|
||||
SubscriberStub
|
||||
>;
|
||||
|
||||
export const SUBSCRIBER_POPULATE: SubscriberPopulate[] = [
|
||||
'labels',
|
||||
'assignedTo',
|
||||
'avatar',
|
||||
];
|
||||
|
||||
@ -177,7 +177,10 @@ describe('BlockService', () => {
|
||||
blockFixture.name === 'hasNextBlocks' ? [hasPreviousBlocks] : [],
|
||||
}));
|
||||
|
||||
expect(blockRepository.findAndPopulate).toHaveBeenCalledWith({});
|
||||
expect(blockRepository.findAndPopulate).toHaveBeenCalledWith(
|
||||
{},
|
||||
undefined,
|
||||
);
|
||||
expect(result).toEqualPayload(blocksWithCategory);
|
||||
});
|
||||
});
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
@ -24,7 +23,7 @@ import { SettingService } from '@/setting/services/setting.service';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { BlockRepository } from '../repositories/block.repository';
|
||||
import { Block, BlockFull } from '../schemas/block.schema';
|
||||
import { Block, BlockFull, BlockPopulate } from '../schemas/block.schema';
|
||||
import { WithUrl } from '../schemas/types/attachment';
|
||||
import { Context } from '../schemas/types/context';
|
||||
import {
|
||||
@ -36,7 +35,7 @@ import { NlpPattern, Pattern, PayloadPattern } from '../schemas/types/pattern';
|
||||
import { Payload, StdQuickReply } from '../schemas/types/quick-reply';
|
||||
|
||||
@Injectable()
|
||||
export class BlockService extends BaseService<Block> {
|
||||
export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
|
||||
constructor(
|
||||
readonly repository: BlockRepository,
|
||||
private readonly contentService: ContentService,
|
||||
@ -49,28 +48,6 @@ export class BlockService extends BaseService<Block> {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and populates blocks based on the specified filters.
|
||||
*
|
||||
* @param filters - Query filters used to specify search criteria for finding blocks.
|
||||
*
|
||||
* @returns A promise that resolves to the populated blocks matching the filters.
|
||||
*/
|
||||
async findAndPopulate(filters: TFilterQuery<Block>) {
|
||||
return await this.repository.findAndPopulate(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and populates a block by ID.
|
||||
*
|
||||
* @param id - The block ID.
|
||||
*
|
||||
* @returns A promise that resolves to the populated block.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a block whose patterns matches the received event
|
||||
*
|
||||
|
||||
@ -17,12 +17,20 @@ import { BaseService } from '@/utils/generics/base-service';
|
||||
import { VIEW_MORE_PAYLOAD } from '../helpers/constants';
|
||||
import { ConversationRepository } from '../repositories/conversation.repository';
|
||||
import { Block, BlockFull } from '../schemas/block.schema';
|
||||
import { Conversation, ConversationFull } from '../schemas/conversation.schema';
|
||||
import {
|
||||
Conversation,
|
||||
ConversationFull,
|
||||
ConversationPopulate,
|
||||
} from '../schemas/conversation.schema';
|
||||
import { OutgoingMessageFormat } from '../schemas/types/message';
|
||||
import { Payload } from '../schemas/types/quick-reply';
|
||||
|
||||
@Injectable()
|
||||
export class ConversationService extends BaseService<Conversation> {
|
||||
export class ConversationService extends BaseService<
|
||||
Conversation,
|
||||
ConversationPopulate,
|
||||
ConversationFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: ConversationRepository,
|
||||
private readonly logger: LoggerService,
|
||||
|
||||
@ -14,10 +14,10 @@ import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { LabelRepository } from '../repositories/label.repository';
|
||||
import { Label } from '../schemas/label.schema';
|
||||
import { Label, LabelFull, LabelPopulate } from '../schemas/label.schema';
|
||||
|
||||
@Injectable()
|
||||
export class LabelService extends BaseService<Label> {
|
||||
export class LabelService extends BaseService<Label, LabelPopulate, LabelFull> {
|
||||
constructor(readonly repository: LabelRepository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
@ -29,11 +29,16 @@ import { SocketResponse } from '@/websocket/utils/socket-response';
|
||||
import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
||||
|
||||
import { MessageRepository } from '../repositories/message.repository';
|
||||
import { MessageFull, MessagePopulate } from '../schemas/message.schema';
|
||||
import { Subscriber } from '../schemas/subscriber.schema';
|
||||
import { AnyMessage } from '../schemas/types/message';
|
||||
|
||||
@Injectable()
|
||||
export class MessageService extends BaseService<AnyMessage> {
|
||||
export class MessageService extends BaseService<
|
||||
AnyMessage,
|
||||
MessagePopulate,
|
||||
MessageFull
|
||||
> {
|
||||
private readonly logger: LoggerService;
|
||||
|
||||
private readonly gateway: WebsocketGateway;
|
||||
|
||||
@ -35,10 +35,18 @@ import { WebsocketGateway } from '@/websocket/websocket.gateway';
|
||||
|
||||
import { SubscriberUpdateDto } from '../dto/subscriber.dto';
|
||||
import { SubscriberRepository } from '../repositories/subscriber.repository';
|
||||
import { Subscriber } from '../schemas/subscriber.schema';
|
||||
import {
|
||||
Subscriber,
|
||||
SubscriberFull,
|
||||
SubscriberPopulate,
|
||||
} from '../schemas/subscriber.schema';
|
||||
|
||||
@Injectable()
|
||||
export class SubscriberService extends BaseService<Subscriber> {
|
||||
export class SubscriberService extends BaseService<
|
||||
Subscriber,
|
||||
SubscriberPopulate,
|
||||
SubscriberFull
|
||||
> {
|
||||
private readonly gateway: WebsocketGateway;
|
||||
|
||||
constructor(
|
||||
|
||||
@ -13,15 +13,15 @@ import path from 'path';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Param,
|
||||
Post,
|
||||
Get,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
UseInterceptors,
|
||||
Patch,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException } from '@nestjs/common/exceptions';
|
||||
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
||||
@ -43,12 +43,22 @@ import { ContentService } from './../services/content.service';
|
||||
import { ContentCreateDto } from '../dto/content.dto';
|
||||
import { ContentTransformInterceptor } from '../interceptors/content.interceptor';
|
||||
import { ContentType } from '../schemas/content-type.schema';
|
||||
import { Content, ContentStub } from '../schemas/content.schema';
|
||||
import {
|
||||
Content,
|
||||
ContentFull,
|
||||
ContentPopulate,
|
||||
ContentStub,
|
||||
} from '../schemas/content.schema';
|
||||
import { preprocessDynamicFields } from '../utilities';
|
||||
|
||||
@UseInterceptors(ContentTransformInterceptor, CsrfInterceptor)
|
||||
@Controller('content')
|
||||
export class ContentController extends BaseController<Content, ContentStub> {
|
||||
export class ContentController extends BaseController<
|
||||
Content,
|
||||
ContentStub,
|
||||
ContentPopulate,
|
||||
ContentFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly contentService: ContentService,
|
||||
private readonly contentTypeService: ContentTypeService,
|
||||
@ -206,7 +216,7 @@ export class ContentController extends BaseController<Content, ContentStub> {
|
||||
)
|
||||
filters: TFilterQuery<Content>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['entity'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.contentService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.contentService.findPage(filters, pageQuery);
|
||||
}
|
||||
@ -241,7 +251,7 @@ export class ContentController extends BaseController<Content, ContentStub> {
|
||||
@Param('id') id: string,
|
||||
@Query(PopulatePipe) populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['entity'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.contentService.findOneAndPopulate(id)
|
||||
: await this.contentService.findOne(id);
|
||||
|
||||
|
||||
@ -31,12 +31,17 @@ import { PageQueryPipe } from '@/utils/pagination/pagination-query.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { MenuCreateDto, MenuQueryDto } from '../dto/menu.dto';
|
||||
import { Menu, MenuStub } from '../schemas/menu.schema';
|
||||
import { Menu, MenuFull, MenuPopulate, MenuStub } from '../schemas/menu.schema';
|
||||
import { MenuService } from '../services/menu.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('menu')
|
||||
export class MenuController extends BaseController<Menu, MenuStub> {
|
||||
export class MenuController extends BaseController<
|
||||
Menu,
|
||||
MenuStub,
|
||||
MenuPopulate,
|
||||
MenuFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly menuService: MenuService,
|
||||
private readonly logger: LoggerService,
|
||||
|
||||
@ -12,7 +12,6 @@ import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BlockService } from '@/chat/services/block.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { ContentType } from '../schemas/content-type.schema';
|
||||
@ -24,11 +23,8 @@ export class ContentTypeRepository extends BaseRepository<ContentType> {
|
||||
@InjectModel(ContentType.name) readonly model: Model<ContentType>,
|
||||
@InjectModel(Content.name) private readonly contentModel: Model<Content>,
|
||||
@Optional() private readonly blockService?: BlockService,
|
||||
@Optional() private readonly logger?: LoggerService,
|
||||
) {
|
||||
super(model, ContentType);
|
||||
this.logger = logger;
|
||||
this.blockService = blockService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -10,53 +10,32 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import {
|
||||
TFilterQuery,
|
||||
Model,
|
||||
Document,
|
||||
HydratedDocument,
|
||||
Model,
|
||||
Query,
|
||||
TFilterQuery,
|
||||
UpdateQuery,
|
||||
UpdateWithAggregationPipeline,
|
||||
HydratedDocument,
|
||||
} from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { Content, ContentFull } from '../schemas/content.schema';
|
||||
import {
|
||||
Content,
|
||||
CONTENT_POPULATE,
|
||||
ContentFull,
|
||||
ContentPopulate,
|
||||
} from '../schemas/content.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ContentRepository extends BaseRepository<Content, 'entity'> {
|
||||
export class ContentRepository extends BaseRepository<
|
||||
Content,
|
||||
ContentPopulate,
|
||||
ContentFull
|
||||
> {
|
||||
constructor(@InjectModel(Content.name) readonly model: Model<Content>) {
|
||||
super(model, Content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of content documents based on the provided filter
|
||||
* and pagination query, and populates the `entity` field.
|
||||
*
|
||||
* @param filter - A filter query for the content documents.
|
||||
* @param pageQuery - Pagination and sorting options for the query.
|
||||
*
|
||||
* @returns A promise that resolves to an array of fully populated `ContentFull` documents.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<Content>,
|
||||
pageQuery: PageQueryDto<Content>,
|
||||
): Promise<ContentFull[]> {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate('entity');
|
||||
return await this.execute(query, ContentFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single content document by its ID and populates the `entity` field.
|
||||
*
|
||||
* @param id - The ID of the content document to retrieve.
|
||||
*
|
||||
* @returns A promise that resolves to the populated `ContentFull` document.
|
||||
*/
|
||||
async findOneAndPopulate(id: string): Promise<ContentFull> {
|
||||
const query = this.findOneQuery(id).populate('entity');
|
||||
return await this.executeOne(query, ContentFull);
|
||||
super(model, Content, CONTENT_POPULATE, ContentFull);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,16 +14,26 @@ import { Document, Model, Query } from 'mongoose';
|
||||
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Menu, MenuDocument, MenuFull } from '../schemas/menu.schema';
|
||||
import {
|
||||
Menu,
|
||||
MENU_POPULATE,
|
||||
MenuDocument,
|
||||
MenuFull,
|
||||
MenuPopulate,
|
||||
} from '../schemas/menu.schema';
|
||||
import { MenuType } from '../schemas/types/menu';
|
||||
|
||||
@Injectable()
|
||||
export class MenuRepository extends BaseRepository<Menu, 'parent'> {
|
||||
export class MenuRepository extends BaseRepository<
|
||||
Menu,
|
||||
MenuPopulate,
|
||||
MenuFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Menu.name) readonly model: Model<Menu>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Menu);
|
||||
super(model, Menu, MENU_POPULATE, MenuFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,16 +117,4 @@ export class MenuRepository extends BaseRepository<Menu, 'parent'> {
|
||||
): Promise<void> {
|
||||
this.eventEmitter.emit('hook:menu:delete', result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a `Menu` document by its ID and populates the `parent` field.
|
||||
*
|
||||
* @param id - The ID of the `Menu` document to be found and populated.
|
||||
*
|
||||
* @returns A promise that resolves to the populated `MenuFull` document.
|
||||
*/
|
||||
async findOneAndPopulate(id: string): Promise<MenuFull> {
|
||||
const query = this.findOneQuery(id).populate('parent');
|
||||
return await this.executeOne(query, MenuFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import mongoose, { Document } from 'mongoose';
|
||||
import { config } from '@/config';
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { ContentType } from './content-type.schema';
|
||||
|
||||
@ -104,3 +105,7 @@ export const ContentModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default ContentModel.schema;
|
||||
|
||||
export type ContentPopulate = keyof TFilterPopulateFields<Content, ContentStub>;
|
||||
|
||||
export const CONTENT_POPULATE: ContentPopulate[] = ['entity'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { MenuType } from './types/menu';
|
||||
|
||||
@ -78,3 +79,7 @@ export const MenuModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default MenuModel.schema;
|
||||
|
||||
export type MenuPopulate = keyof TFilterPopulateFields<Menu, MenuStub>;
|
||||
|
||||
export const MENU_POPULATE: MenuPopulate[] = ['parent'];
|
||||
|
||||
@ -17,13 +17,20 @@ import { StdOutgoingListMessage } from '@/chat/schemas/types/message';
|
||||
import { ContentOptions } from '@/chat/schemas/types/options';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { ContentRepository } from '../repositories/content.repository';
|
||||
import { Content } from '../schemas/content.schema';
|
||||
import {
|
||||
Content,
|
||||
ContentFull,
|
||||
ContentPopulate,
|
||||
} from '../schemas/content.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ContentService extends BaseService<Content> {
|
||||
export class ContentService extends BaseService<
|
||||
Content,
|
||||
ContentPopulate,
|
||||
ContentFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: ContentRepository,
|
||||
private readonly attachmentService: AttachmentService,
|
||||
@ -32,33 +39,6 @@ export class ContentService extends BaseService<Content> {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a content item by its ID and populates its related fields.
|
||||
*
|
||||
* @param id - The ID of the content to retrieve.
|
||||
*
|
||||
* @return The populated content entity.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a page of content items based on filters and pagination options,
|
||||
* and populates their related fields.
|
||||
*
|
||||
* @param filters - The query filters to apply.
|
||||
* @param pageQuery - The pagination and sorting options.
|
||||
*
|
||||
* @return A list of populated content entities.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<Content>,
|
||||
pageQuery: PageQueryDto<Content>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a text search on the content repository.
|
||||
*
|
||||
|
||||
@ -17,24 +17,22 @@ import {
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Cache } from 'cache-manager';
|
||||
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { MENU_CACHE_KEY } from '@/utils/constants/cache';
|
||||
import { Cacheable } from '@/utils/decorators/cacheable.decorator';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { MenuCreateDto } from '../dto/menu.dto';
|
||||
import { MenuRepository } from '../repositories/menu.repository';
|
||||
import { Menu } from '../schemas/menu.schema';
|
||||
import { Menu, MenuFull, MenuPopulate } from '../schemas/menu.schema';
|
||||
import { AnyMenu, MenuTree, MenuType } from '../schemas/types/menu';
|
||||
|
||||
@Injectable()
|
||||
export class MenuService extends BaseService<Menu> {
|
||||
export class MenuService extends BaseService<Menu, MenuPopulate, MenuFull> {
|
||||
private RootSymbol: symbol = Symbol('RootMenu');
|
||||
|
||||
constructor(
|
||||
readonly repository: MenuRepository,
|
||||
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
@ -8,19 +8,19 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Controller,
|
||||
Delete,
|
||||
Param,
|
||||
Query,
|
||||
Get,
|
||||
HttpCode,
|
||||
NotFoundException,
|
||||
UseInterceptors,
|
||||
MethodNotAllowedException,
|
||||
InternalServerErrorException,
|
||||
MethodNotAllowedException,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
@ -34,14 +34,21 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { NlpEntityCreateDto } from '../dto/nlp-entity.dto';
|
||||
import { NlpEntity, NlpEntityStub } from '../schemas/nlp-entity.schema';
|
||||
import {
|
||||
NlpEntity,
|
||||
NlpEntityFull,
|
||||
NlpEntityPopulate,
|
||||
NlpEntityStub,
|
||||
} from '../schemas/nlp-entity.schema';
|
||||
import { NlpEntityService } from '../services/nlp-entity.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('nlpentity')
|
||||
export class NlpEntityController extends BaseController<
|
||||
NlpEntity,
|
||||
NlpEntityStub
|
||||
NlpEntityStub,
|
||||
NlpEntityPopulate,
|
||||
NlpEntityFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly nlpEntityService: NlpEntityService,
|
||||
@ -99,7 +106,7 @@ export class NlpEntityController extends BaseController<
|
||||
@Param('id') id: string,
|
||||
@Query(PopulatePipe) populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['values'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.nlpEntityService.findOneAndPopulate(id)
|
||||
: await this.nlpEntityService.findOne(id);
|
||||
if (!doc) {
|
||||
@ -127,7 +134,7 @@ export class NlpEntityController extends BaseController<
|
||||
@Query(new SearchFilterPipe<NlpEntity>({ allowedFields: ['name', 'doc'] }))
|
||||
filters: TFilterQuery<NlpEntity>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['values'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.nlpEntityService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.nlpEntityService.findPage(filters, pageQuery);
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ import { NlpSampleCreateDto, NlpSampleDto } from '../dto/nlp-sample.dto';
|
||||
import {
|
||||
NlpSample,
|
||||
NlpSampleFull,
|
||||
NlpSamplePopulate,
|
||||
NlpSampleStub,
|
||||
} from '../schemas/nlp-sample.schema';
|
||||
import { NlpSampleEntityValue, NlpSampleState } from '../schemas/types';
|
||||
@ -58,7 +59,9 @@ import { NlpService } from '../services/nlp.service';
|
||||
@Controller('nlpsample')
|
||||
export class NlpSampleController extends BaseController<
|
||||
NlpSample,
|
||||
NlpSampleStub
|
||||
NlpSampleStub,
|
||||
NlpSamplePopulate,
|
||||
NlpSampleFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly nlpSampleService: NlpSampleService,
|
||||
@ -217,7 +220,7 @@ export class NlpSampleController extends BaseController<
|
||||
@Param('id') id: string,
|
||||
@Query(PopulatePipe) populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['entities'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.nlpSampleService.findOneAndPopulate(id)
|
||||
: await this.nlpSampleService.findOne(id);
|
||||
if (!doc) {
|
||||
@ -243,7 +246,7 @@ export class NlpSampleController extends BaseController<
|
||||
@Query(new SearchFilterPipe<NlpSample>({ allowedFields: ['text', 'type'] }))
|
||||
filters: TFilterQuery<NlpSample>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['entities'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.nlpSampleService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.nlpSampleService.findPage(filters, pageQuery);
|
||||
}
|
||||
|
||||
@ -32,13 +32,23 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { NlpValueCreateDto, NlpValueUpdateDto } from '../dto/nlp-value.dto';
|
||||
import { NlpValue, NlpValueStub } from '../schemas/nlp-value.schema';
|
||||
import {
|
||||
NlpValue,
|
||||
NlpValueFull,
|
||||
NlpValuePopulate,
|
||||
NlpValueStub,
|
||||
} from '../schemas/nlp-value.schema';
|
||||
import { NlpEntityService } from '../services/nlp-entity.service';
|
||||
import { NlpValueService } from '../services/nlp-value.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('nlpvalue')
|
||||
export class NlpValueController extends BaseController<NlpValue, NlpValueStub> {
|
||||
export class NlpValueController extends BaseController<
|
||||
NlpValue,
|
||||
NlpValueStub,
|
||||
NlpValuePopulate,
|
||||
NlpValueFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly nlpValueService: NlpValueService,
|
||||
private readonly nlpEntityService: NlpEntityService,
|
||||
@ -103,7 +113,7 @@ export class NlpValueController extends BaseController<NlpValue, NlpValueStub> {
|
||||
@Param('id') id: string,
|
||||
@Query(PopulatePipe) populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['entity'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.nlpValueService.findOneAndPopulate(id)
|
||||
: await this.nlpValueService.findOne(id);
|
||||
if (!doc) {
|
||||
@ -135,7 +145,7 @@ export class NlpValueController extends BaseController<NlpValue, NlpValueStub> {
|
||||
)
|
||||
filters: TFilterQuery<NlpValue>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['entity'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.nlpValueService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.nlpValueService.findPage(filters, pageQuery);
|
||||
}
|
||||
|
||||
@ -13,21 +13,29 @@ import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Document, Model, Query, TFilterQuery, Types } from 'mongoose';
|
||||
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
|
||||
import { NlpValueRepository } from './nlp-value.repository';
|
||||
import { NlpEntity, NlpEntityFull } from '../schemas/nlp-entity.schema';
|
||||
import {
|
||||
NLP_ENTITY_POPULATE,
|
||||
NlpEntity,
|
||||
NlpEntityFull,
|
||||
NlpEntityPopulate,
|
||||
} from '../schemas/nlp-entity.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpEntityRepository extends BaseRepository<NlpEntity, 'values'> {
|
||||
export class NlpEntityRepository extends BaseRepository<
|
||||
NlpEntity,
|
||||
NlpEntityPopulate,
|
||||
NlpEntityFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(NlpEntity.name) readonly model: Model<NlpEntity>,
|
||||
private readonly nlpValueRepository: NlpValueRepository,
|
||||
private readonly nlpSampleEntityRepository: NlpSampleEntityRepository,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, NlpEntity);
|
||||
super(model, NlpEntity, NLP_ENTITY_POPULATE, NlpEntityFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,43 +114,4 @@ export class NlpEntityRepository extends BaseRepository<NlpEntity, 'values'> {
|
||||
throw new Error('Attempted to delete NLP entity using unknown criteria');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all NLP entities and populates related `values`.
|
||||
*
|
||||
* @returns Promise containing an array of fully populated NLP entities.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
const query = this.findAllQuery().populate(['values']);
|
||||
return await this.execute(query, NlpEntityFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of NLP entities based on filter criteria,
|
||||
* and populates related `values`.
|
||||
*
|
||||
* @param filter Filter criteria for NLP entities.
|
||||
* @param pageQuery Pagination query.
|
||||
*
|
||||
* @returns Promise containing the paginated result of fully populated NLP entities.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<NlpEntity>,
|
||||
pageQuery: PageQueryDto<NlpEntity>,
|
||||
) {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate(['values']);
|
||||
return await this.execute(query, NlpEntityFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single NLP entity by its ID and populates related `values`.
|
||||
*
|
||||
* @param id The ID of the NLP entity.
|
||||
*
|
||||
* @returns Promise containing the fully populated NLP entity.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate(['values']);
|
||||
return await this.executeOne(query, NlpEntityFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,55 +9,31 @@
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { TFilterQuery, Model } from 'mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import {
|
||||
NLP_SAMPLE_ENTITY_POPULATE,
|
||||
NlpSampleEntity,
|
||||
NlpSampleEntityFull,
|
||||
NlpSampleEntityPopulate,
|
||||
} from '../schemas/nlp-sample-entity.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpSampleEntityRepository extends BaseRepository<
|
||||
NlpSampleEntity,
|
||||
'entity' | 'value' | 'sample'
|
||||
NlpSampleEntityPopulate,
|
||||
NlpSampleEntityFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(NlpSampleEntity.name) readonly model: Model<NlpSampleEntity>,
|
||||
) {
|
||||
super(model, NlpSampleEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a paginated set of `NlpSampleEntity` documents based on the provided filter
|
||||
* and page query. Also populates related fields such as `entity`, `value`, and `sample`.
|
||||
*
|
||||
* @param filter - The query filter for retrieving documents.
|
||||
* @param pageQuery - The pagination options.
|
||||
* @returns The paginated results with populated fields.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<NlpSampleEntity>,
|
||||
pageQuery: PageQueryDto<NlpSampleEntity>,
|
||||
) {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate([
|
||||
'entity',
|
||||
'value',
|
||||
'sample',
|
||||
]);
|
||||
return await this.execute(query, NlpSampleEntityFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single `NlpSampleEntity` document by ID and populates the related fields `entity`, `value`, and `sample`.
|
||||
*
|
||||
* @param id - The ID of the document to retrieve.
|
||||
* @returns The document with populated fields.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate(['entity', 'value', 'sample']);
|
||||
return await this.executeOne(query, NlpSampleEntityFull);
|
||||
super(
|
||||
model,
|
||||
NlpSampleEntity,
|
||||
NLP_SAMPLE_ENTITY_POPULATE,
|
||||
NlpSampleEntityFull,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,18 +12,26 @@ import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
|
||||
import { NlpSample, NlpSampleFull } from '../schemas/nlp-sample.schema';
|
||||
import {
|
||||
NLP_SAMPLE_POPULATE,
|
||||
NlpSample,
|
||||
NlpSampleFull,
|
||||
NlpSamplePopulate,
|
||||
} from '../schemas/nlp-sample.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpSampleRepository extends BaseRepository<NlpSample, 'entities'> {
|
||||
export class NlpSampleRepository extends BaseRepository<
|
||||
NlpSample,
|
||||
NlpSamplePopulate,
|
||||
NlpSampleFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(NlpSample.name) readonly model: Model<NlpSample>,
|
||||
private readonly nlpSampleEntityRepository: NlpSampleEntityRepository,
|
||||
) {
|
||||
super(model, NlpSample);
|
||||
super(model, NlpSample, NLP_SAMPLE_POPULATE, NlpSampleFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,56 +60,4 @@ export class NlpSampleRepository extends BaseRepository<NlpSample, 'entities'> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of NLP samples and populates the related entities.
|
||||
*
|
||||
* @param filter Query filter used to retrieve NLP samples.
|
||||
* @param pageQuery Pagination details for the query.
|
||||
*
|
||||
* @returns A promise that resolves to a paginated list of `NlpSampleFull` objects.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<NlpSample>,
|
||||
pageQuery: PageQueryDto<NlpSample>,
|
||||
): Promise<NlpSampleFull[]> {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate(['entities']);
|
||||
return await this.execute(query, NlpSampleFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all NLP samples that match the filter and populates related entities.
|
||||
*
|
||||
* @param filter Query filter used to retrieve NLP samples.
|
||||
*
|
||||
* @returns A promise that resolves to a list of `NlpSampleFull` objects.
|
||||
*/
|
||||
async findAndPopulate(
|
||||
filter: TFilterQuery<NlpSample>,
|
||||
): Promise<NlpSampleFull[]> {
|
||||
const query = this.findQuery(filter).populate(['entities']);
|
||||
return await this.execute(query, NlpSampleFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an NLP sample by its ID and populates related entities.
|
||||
*
|
||||
* @param id The ID of the NLP sample to retrieve.
|
||||
*
|
||||
* @returns A promise that resolves to the `NlpSampleFull` object.
|
||||
*/
|
||||
async findOneAndPopulate(id: string): Promise<NlpSampleFull> {
|
||||
const query = this.findOneQuery(id).populate(['entities']);
|
||||
return await this.executeOne(query, NlpSampleFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all NLP samples and populates related entities.
|
||||
*
|
||||
* @returns A promise that resolves to a list of all `NlpSampleFull` objects.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
const query = this.findAllQuery().populate(['entities']);
|
||||
return await this.execute(query, NlpSampleFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,23 +13,28 @@ import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Document, Model, Query, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
|
||||
import {
|
||||
NLP_VALUE_POPULATE,
|
||||
NlpValue,
|
||||
NlpValueDocument,
|
||||
NlpValueFull,
|
||||
NlpValuePopulate,
|
||||
} from '../schemas/nlp-value.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpValueRepository extends BaseRepository<NlpValue, 'entity'> {
|
||||
export class NlpValueRepository extends BaseRepository<
|
||||
NlpValue,
|
||||
NlpValuePopulate,
|
||||
NlpValueFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(NlpValue.name) readonly model: Model<NlpValue>,
|
||||
private readonly nlpSampleEntityRepository: NlpSampleEntityRepository,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, NlpValue);
|
||||
super(model, NlpValue, NLP_VALUE_POPULATE, NlpValueFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,33 +105,4 @@ export class NlpValueRepository extends BaseRepository<NlpValue, 'entity'> {
|
||||
throw new Error('Attempted to delete a NLP value using unknown criteria');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and paginates NLP values based on the provided filter and page query,
|
||||
* populating related entities.
|
||||
*
|
||||
* @param filter - The filter query used to search for NLP values.
|
||||
* @param pageQuery - The pagination query details.
|
||||
*
|
||||
* @returns A list of populated NLP values for the requested page.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<NlpValue>,
|
||||
pageQuery: PageQueryDto<NlpValue>,
|
||||
): Promise<NlpValueFull[]> {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate(['entity']);
|
||||
return await this.execute(query, NlpValueFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and populates a single NLP value by its ID.
|
||||
*
|
||||
* @param id - The ID of the NLP value to find.
|
||||
*
|
||||
* @returns The populated NLP value document.
|
||||
*/
|
||||
async findOneAndPopulate(id: string): Promise<NlpValueFull> {
|
||||
const query = this.findOneQuery(id).populate(['entity']);
|
||||
return await this.executeOne(query, NlpValueFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { NlpValue } from './nlp-value.schema';
|
||||
import { NlpEntityMap } from './types';
|
||||
@ -95,3 +96,10 @@ NlpEntityModel.schema.virtual('values', {
|
||||
});
|
||||
|
||||
export default NlpEntityModel.schema;
|
||||
|
||||
export type NlpEntityPopulate = keyof TFilterPopulateFields<
|
||||
NlpEntity,
|
||||
NlpEntityStub
|
||||
>;
|
||||
|
||||
export const NLP_ENTITY_POPULATE: NlpEntityPopulate[] = ['values'];
|
||||
|
||||
@ -12,6 +12,7 @@ import { Transform, Type } from 'class-transformer';
|
||||
import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { NlpEntity } from './nlp-entity.schema';
|
||||
import { NlpSample } from './nlp-sample.schema';
|
||||
@ -106,3 +107,14 @@ export const NlpSampleEntityModel: ModelDefinition = {
|
||||
};
|
||||
|
||||
export default NlpSampleEntityModel.schema;
|
||||
|
||||
export type NlpSampleEntityPopulate = keyof TFilterPopulateFields<
|
||||
NlpSampleEntity,
|
||||
NlpSampleEntityStub
|
||||
>;
|
||||
|
||||
export const NLP_SAMPLE_ENTITY_POPULATE: NlpSampleEntityPopulate[] = [
|
||||
'entity',
|
||||
'value',
|
||||
'sample',
|
||||
];
|
||||
|
||||
@ -7,12 +7,13 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Exclude, Type } from 'class-transformer';
|
||||
import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { NlpSampleEntity } from './nlp-sample-entity.schema';
|
||||
import { NlpSampleState } from './types';
|
||||
@ -68,3 +69,10 @@ NlpSampleModel.schema.virtual('entities', {
|
||||
});
|
||||
|
||||
export default NlpSampleModel.schema;
|
||||
|
||||
export type NlpSamplePopulate = keyof TFilterPopulateFields<
|
||||
NlpSample,
|
||||
NlpSampleStub
|
||||
>;
|
||||
|
||||
export const NLP_SAMPLE_POPULATE: NlpSamplePopulate[] = ['entities'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { NlpEntity, NlpEntityFull } from './nlp-entity.schema';
|
||||
import { NlpValueMap } from './types';
|
||||
@ -105,3 +106,10 @@ export const NlpValueModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default NlpValueModel.schema;
|
||||
|
||||
export type NlpValuePopulate = keyof TFilterPopulateFields<
|
||||
NlpValue,
|
||||
NlpValueStub
|
||||
>;
|
||||
|
||||
export const NLP_VALUE_POPULATE: NlpValuePopulate[] = ['entity'];
|
||||
|
||||
@ -12,10 +12,18 @@ import { Injectable } from '@nestjs/common';
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
|
||||
import { NlpEntity } from '../schemas/nlp-entity.schema';
|
||||
import {
|
||||
NlpEntity,
|
||||
NlpEntityFull,
|
||||
NlpEntityPopulate,
|
||||
} from '../schemas/nlp-entity.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpEntitySeeder extends BaseSeeder<NlpEntity> {
|
||||
export class NlpEntitySeeder extends BaseSeeder<
|
||||
NlpEntity,
|
||||
NlpEntityPopulate,
|
||||
NlpEntityFull
|
||||
> {
|
||||
constructor(nlpEntityRepository: NlpEntityRepository) {
|
||||
super(nlpEntityRepository);
|
||||
}
|
||||
|
||||
@ -14,10 +14,18 @@ import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
|
||||
import { NlpValueRepository } from '../repositories/nlp-value.repository';
|
||||
import { NlpValue } from '../schemas/nlp-value.schema';
|
||||
import {
|
||||
NlpValue,
|
||||
NlpValueFull,
|
||||
NlpValuePopulate,
|
||||
} from '../schemas/nlp-value.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpValueSeeder extends BaseSeeder<NlpValue> {
|
||||
export class NlpValueSeeder extends BaseSeeder<
|
||||
NlpValue,
|
||||
NlpValuePopulate,
|
||||
NlpValueFull
|
||||
> {
|
||||
constructor(
|
||||
nlpValueRepository: NlpValueRepository,
|
||||
private readonly nlpEntityRepository: NlpEntityRepository,
|
||||
|
||||
@ -8,19 +8,25 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpValueService } from './nlp-value.service';
|
||||
import { Lookup } from '../dto/nlp-entity.dto';
|
||||
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
|
||||
import { NlpEntity } from '../schemas/nlp-entity.schema';
|
||||
import {
|
||||
NlpEntity,
|
||||
NlpEntityFull,
|
||||
NlpEntityPopulate,
|
||||
} from '../schemas/nlp-entity.schema';
|
||||
import { NlpSampleEntityValue } from '../schemas/types';
|
||||
|
||||
@Injectable()
|
||||
export class NlpEntityService extends BaseService<NlpEntity> {
|
||||
export class NlpEntityService extends BaseService<
|
||||
NlpEntity,
|
||||
NlpEntityPopulate,
|
||||
NlpEntityFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: NlpEntityRepository,
|
||||
private readonly nlpValueService: NlpValueService,
|
||||
@ -39,41 +45,6 @@ export class NlpEntityService extends BaseService<NlpEntity> {
|
||||
return await this.repository.deleteOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an entity by its ID and populates related data.
|
||||
*
|
||||
* @param id - The ID of the entity to find.
|
||||
*
|
||||
* @returns A promise that resolves with the populated entity.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all entities and populates related data.
|
||||
*
|
||||
* @returns A promise that resolves with the populated list of entities.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
return await this.repository.findAllAndPopulate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities based on the specified filters and pagination, and populates related data.
|
||||
*
|
||||
* @param filters - The filters to apply to the query.
|
||||
* @param pageQuery - The pagination and sorting options for the query.
|
||||
*
|
||||
* @returns A promise that resolves with the populated page of entities.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<NlpEntity>,
|
||||
pageQuery: PageQueryDto<NlpEntity>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores new entities based on the sample text and sample entities.
|
||||
* Deletes all values relative to this entity before deleting the entity itself.
|
||||
|
||||
@ -8,20 +8,26 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpEntityService } from './nlp-entity.service';
|
||||
import { NlpValueService } from './nlp-value.service';
|
||||
import { NlpSampleEntityRepository } from '../repositories/nlp-sample-entity.repository';
|
||||
import { NlpSampleEntity } from '../schemas/nlp-sample-entity.schema';
|
||||
import {
|
||||
NlpSampleEntity,
|
||||
NlpSampleEntityFull,
|
||||
NlpSampleEntityPopulate,
|
||||
} from '../schemas/nlp-sample-entity.schema';
|
||||
import { NlpSample } from '../schemas/nlp-sample.schema';
|
||||
import { NlpSampleEntityValue } from '../schemas/types';
|
||||
|
||||
@Injectable()
|
||||
export class NlpSampleEntityService extends BaseService<NlpSampleEntity> {
|
||||
export class NlpSampleEntityService extends BaseService<
|
||||
NlpSampleEntity,
|
||||
NlpSampleEntityPopulate,
|
||||
NlpSampleEntityFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: NlpSampleEntityRepository,
|
||||
private readonly nlpEntityService: NlpEntityService,
|
||||
@ -30,34 +36,6 @@ export class NlpSampleEntityService extends BaseService<NlpSampleEntity> {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single NLP sample entity by its ID and populates related
|
||||
* entities for the retrieved sample.
|
||||
*
|
||||
* @param id The ID of the NLP sample entity to find and populate.
|
||||
*
|
||||
* @returns The populated NLP sample entity.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of NLP sample entities based on filters, and
|
||||
* populates related entities for each retrieved sample entity.
|
||||
*
|
||||
* @param filters Filters to apply when searching for the entities.
|
||||
* @param pageQuery Query parameters for pagination.
|
||||
*
|
||||
* @returns A paginated list of populated NLP sample entities.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<NlpSampleEntity>,
|
||||
pageQuery: PageQueryDto<NlpSampleEntity>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new sample entities to the corresponding training sample and returns
|
||||
* the created sample entities. It handles the storage of new entities and
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import {
|
||||
CommonExample,
|
||||
@ -18,15 +17,22 @@ import {
|
||||
LookupTable,
|
||||
} from '@/extensions/helpers/nlp/default/types';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpSampleRepository } from '../repositories/nlp-sample.repository';
|
||||
import { NlpEntity, NlpEntityFull } from '../schemas/nlp-entity.schema';
|
||||
import { NlpSample, NlpSampleFull } from '../schemas/nlp-sample.schema';
|
||||
import {
|
||||
NlpSample,
|
||||
NlpSampleFull,
|
||||
NlpSamplePopulate,
|
||||
} from '../schemas/nlp-sample.schema';
|
||||
import { NlpValue } from '../schemas/nlp-value.schema';
|
||||
|
||||
@Injectable()
|
||||
export class NlpSampleService extends BaseService<NlpSample> {
|
||||
export class NlpSampleService extends BaseService<
|
||||
NlpSample,
|
||||
NlpSamplePopulate,
|
||||
NlpSampleFull
|
||||
> {
|
||||
constructor(readonly repository: NlpSampleRepository) {
|
||||
super(repository);
|
||||
}
|
||||
@ -42,52 +48,6 @@ export class NlpSampleService extends BaseService<NlpSample> {
|
||||
return await this.repository.deleteOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single NLP sample by its ID and populates related data.
|
||||
*
|
||||
* @param id - The unique identifier of the NLP sample to find.
|
||||
*
|
||||
* @returns A promise resolving to the found sample with populated fields.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a page of NLP samples based on filters and populates related data.
|
||||
*
|
||||
* @param filters - Query filters to apply when searching for samples.
|
||||
* @param pageQuery - Pagination and sorting options for the query.
|
||||
*
|
||||
* @returns A promise resolving to the paginated results with populated fields.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<NlpSample>,
|
||||
pageQuery: PageQueryDto<NlpSample>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds multiple NLP samples based on filters and populates related data.
|
||||
*
|
||||
* @param filters - Query filters to apply when searching for samples.
|
||||
*
|
||||
* @returns A promise resolving to the found samples with populated fields.
|
||||
*/
|
||||
async findAndPopulate(filters: TFilterQuery<NlpSample>) {
|
||||
return await this.repository.findAndPopulate(filters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all NLP samples and populates related data.
|
||||
*
|
||||
* @returns A promise resolving to all samples with populated fields.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
return await this.repository.findAllAndPopulate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a set of NLP samples into the Rasa NLU-compatible training dataset format.
|
||||
*
|
||||
|
||||
@ -8,20 +8,26 @@
|
||||
*/
|
||||
|
||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { NlpEntityService } from './nlp-entity.service';
|
||||
import { NlpValueCreateDto, NlpValueUpdateDto } from '../dto/nlp-value.dto';
|
||||
import { NlpValueRepository } from '../repositories/nlp-value.repository';
|
||||
import { NlpEntity } from '../schemas/nlp-entity.schema';
|
||||
import { NlpValue } from '../schemas/nlp-value.schema';
|
||||
import {
|
||||
NlpValue,
|
||||
NlpValueFull,
|
||||
NlpValuePopulate,
|
||||
} from '../schemas/nlp-value.schema';
|
||||
import { NlpSampleEntityValue } from '../schemas/types';
|
||||
|
||||
@Injectable()
|
||||
export class NlpValueService extends BaseService<NlpValue> {
|
||||
export class NlpValueService extends BaseService<
|
||||
NlpValue,
|
||||
NlpValuePopulate,
|
||||
NlpValueFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: NlpValueRepository,
|
||||
@Inject(forwardRef(() => NlpEntityService))
|
||||
@ -41,32 +47,6 @@ export class NlpValueService extends BaseService<NlpValue> {
|
||||
return await this.repository.deleteOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an NLP value by its ID and populates related entities.
|
||||
*
|
||||
* @param id The ID of the NLP value to find.
|
||||
*
|
||||
* @returns A promise that resolves with the populated NLP value.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a page of NLP values based on filters, and populates related entities.
|
||||
*
|
||||
* @param filters The filters to apply when searching for NLP values.
|
||||
* @param pageQuery Pagination information such as page number and size.
|
||||
*
|
||||
* @returns A promise that resolves with a page of populated NLP values.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<NlpValue>,
|
||||
pageQuery: PageQueryDto<NlpValue>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new NLP values or updates existing ones based on the provided training sample.
|
||||
* This method handles both the creation of new values and the addition of synonyms.
|
||||
|
||||
@ -92,10 +92,8 @@ describe('ModelController', () => {
|
||||
describe('find', () => {
|
||||
it('should find models', async () => {
|
||||
jest.spyOn(modelService, 'findAndPopulate');
|
||||
const result = await modelController.find([], {});
|
||||
expect(modelService.findAndPopulate).toHaveBeenCalledWith({}, [
|
||||
'permissions',
|
||||
]);
|
||||
const result = await modelController.find(['permissions'], {});
|
||||
expect(modelService.findAndPopulate).toHaveBeenCalledWith({});
|
||||
expect(result).toEqualPayload(
|
||||
modelFixtures.map((modelFixture) => ({
|
||||
...modelFixture,
|
||||
@ -122,9 +120,7 @@ describe('ModelController', () => {
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
expect(modelService.findAndPopulate).toHaveBeenCalledWith({}, [
|
||||
'permissions',
|
||||
]);
|
||||
expect(modelService.findAndPopulate).toHaveBeenCalledWith({});
|
||||
expect(result).toEqualPayload(modelsWithPermissionsAndUsers);
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,11 +13,21 @@ import { TFilterQuery } from 'mongoose';
|
||||
import { BaseController } from '@/utils/generics/base-controller';
|
||||
import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
|
||||
import { Model, ModelStub } from '../schemas/model.schema';
|
||||
import {
|
||||
Model,
|
||||
ModelFull,
|
||||
ModelPopulate,
|
||||
ModelStub,
|
||||
} from '../schemas/model.schema';
|
||||
import { ModelService } from '../services/model.service';
|
||||
|
||||
@Controller('model')
|
||||
export class ModelController extends BaseController<Model, ModelStub> {
|
||||
export class ModelController extends BaseController<
|
||||
Model,
|
||||
ModelStub,
|
||||
ModelPopulate,
|
||||
ModelFull
|
||||
> {
|
||||
constructor(private readonly modelService: ModelService) {
|
||||
super(modelService);
|
||||
}
|
||||
@ -39,9 +49,8 @@ export class ModelController extends BaseController<Model, ModelStub> {
|
||||
populate: string[],
|
||||
filters: TFilterQuery<Model>,
|
||||
) {
|
||||
return await this.modelService.findAndPopulate(
|
||||
filters,
|
||||
this.canPopulate(populate, ['permissions']) ? populate : ['permissions'],
|
||||
);
|
||||
return this.canPopulate(populate)
|
||||
? await this.modelService.findAndPopulate(filters)
|
||||
: await this.modelService.find(filters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,12 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { PermissionCreateDto } from '../dto/permission.dto';
|
||||
import { Permission, PermissionStub } from '../schemas/permission.schema';
|
||||
import {
|
||||
Permission,
|
||||
PermissionFull,
|
||||
PermissionPopulate,
|
||||
PermissionStub,
|
||||
} from '../schemas/permission.schema';
|
||||
import { ModelService } from '../services/model.service';
|
||||
import { PermissionService } from '../services/permission.service';
|
||||
import { RoleService } from '../services/role.service';
|
||||
@ -38,7 +43,9 @@ import { RoleService } from '../services/role.service';
|
||||
@Controller('permission')
|
||||
export class PermissionController extends BaseController<
|
||||
Permission,
|
||||
PermissionStub
|
||||
PermissionStub,
|
||||
PermissionPopulate,
|
||||
PermissionFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly permissionService: PermissionService,
|
||||
@ -68,7 +75,7 @@ export class PermissionController extends BaseController<
|
||||
)
|
||||
filters: TFilterQuery<Permission>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['model', 'role'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.permissionService.findAndPopulate(filters)
|
||||
: await this.permissionService.find(filters);
|
||||
}
|
||||
|
||||
@ -32,12 +32,17 @@ import { PopulatePipe } from '@/utils/pipes/populate.pipe';
|
||||
import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe';
|
||||
|
||||
import { RoleCreateDto, RoleUpdateDto } from '../dto/role.dto';
|
||||
import { Role, RoleStub } from '../schemas/role.schema';
|
||||
import { Role, RoleFull, RolePopulate, RoleStub } from '../schemas/role.schema';
|
||||
import { RoleService } from '../services/role.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('role')
|
||||
export class RoleController extends BaseController<Role, RoleStub> {
|
||||
export class RoleController extends BaseController<
|
||||
Role,
|
||||
RoleStub,
|
||||
RolePopulate,
|
||||
RoleFull
|
||||
> {
|
||||
constructor(
|
||||
private readonly roleService: RoleService,
|
||||
private readonly logger: LoggerService,
|
||||
@ -58,7 +63,7 @@ export class RoleController extends BaseController<Role, RoleStub> {
|
||||
@Query(new SearchFilterPipe<Role>({ allowedFields: ['name'] }))
|
||||
filters: TFilterQuery<Role>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['permissions', 'users'])
|
||||
return this.canPopulate(populate)
|
||||
? await this.roleService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.roleService.findPage(filters, pageQuery);
|
||||
}
|
||||
@ -89,7 +94,7 @@ export class RoleController extends BaseController<Role, RoleStub> {
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
) {
|
||||
const doc = this.canPopulate(populate, ['permissions', 'users'])
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.roleService.findOneAndPopulate(id)
|
||||
: await this.roleService.findOne(id);
|
||||
|
||||
|
||||
@ -148,9 +148,7 @@ describe('UserController', () => {
|
||||
it('should find one user and populate its roles', async () => {
|
||||
jest.spyOn(userService, 'findOneAndPopulate');
|
||||
const result = await userController.findOne(user.id, ['roles']);
|
||||
expect(userService.findOneAndPopulate).toHaveBeenCalledWith(user.id, [
|
||||
'roles',
|
||||
]);
|
||||
expect(userService.findOneAndPopulate).toHaveBeenCalledWith(user.id);
|
||||
expect(result).toEqualPayload(
|
||||
{
|
||||
...userFixtures.find(({ username }) => username === 'admin'),
|
||||
@ -166,9 +164,7 @@ describe('UserController', () => {
|
||||
|
||||
it('should find users, and for each user populate the corresponding roles', async () => {
|
||||
jest.spyOn(userService, 'findPageAndPopulate');
|
||||
const result = await userService.findPageAndPopulate({}, pageQuery, [
|
||||
'roles',
|
||||
]);
|
||||
const result = await userService.findPageAndPopulate({}, pageQuery);
|
||||
|
||||
const usersWithRoles = userFixtures.reduce((acc, currUser) => {
|
||||
acc.push({
|
||||
@ -181,7 +177,6 @@ describe('UserController', () => {
|
||||
expect(userService.findPageAndPopulate).toHaveBeenCalledWith(
|
||||
{},
|
||||
pageQuery,
|
||||
['roles'],
|
||||
);
|
||||
expect(result).toEqualPayload(usersWithRoles, [
|
||||
...IGNORED_FIELDS,
|
||||
|
||||
@ -48,7 +48,7 @@ import {
|
||||
UserResetPasswordDto,
|
||||
UserUpdateStateAndRolesDto,
|
||||
} from '../dto/user.dto';
|
||||
import { User, UserStub } from '../schemas/user.schema';
|
||||
import { User, UserFull, UserPopulate, UserStub } from '../schemas/user.schema';
|
||||
import { InvitationService } from '../services/invitation.service';
|
||||
import { PasswordResetService } from '../services/passwordReset.service';
|
||||
import { PermissionService } from '../services/permission.service';
|
||||
@ -58,7 +58,12 @@ import { ValidateAccountService } from '../services/validate-account.service';
|
||||
|
||||
@UseInterceptors(CsrfInterceptor)
|
||||
@Controller('user')
|
||||
export class ReadOnlyUserController extends BaseController<User, UserStub> {
|
||||
export class ReadOnlyUserController extends BaseController<
|
||||
User,
|
||||
UserStub,
|
||||
UserPopulate,
|
||||
UserFull
|
||||
> {
|
||||
constructor(
|
||||
protected readonly userService: UserService,
|
||||
protected readonly roleService: RoleService,
|
||||
@ -122,7 +127,6 @@ export class ReadOnlyUserController extends BaseController<User, UserStub> {
|
||||
|
||||
const currentUser = await this.userService.findOneAndPopulate(
|
||||
req.user.id as string,
|
||||
['roles'],
|
||||
);
|
||||
const currentPermissions = await this.permissionService.findAndPopulate({
|
||||
role: {
|
||||
@ -165,8 +169,8 @@ export class ReadOnlyUserController extends BaseController<User, UserStub> {
|
||||
)
|
||||
filters: TFilterQuery<User>,
|
||||
) {
|
||||
return this.canPopulate(populate, ['roles', 'avatar'])
|
||||
? await this.userService.findPageAndPopulate(filters, pageQuery, populate)
|
||||
return this.canPopulate(populate)
|
||||
? await this.userService.findPageAndPopulate(filters, pageQuery)
|
||||
: await this.userService.find(filters);
|
||||
}
|
||||
|
||||
@ -201,10 +205,9 @@ export class ReadOnlyUserController extends BaseController<User, UserStub> {
|
||||
@Query(PopulatePipe)
|
||||
populate: string[],
|
||||
) {
|
||||
const doc = await this.userService.findOneAndPopulate(
|
||||
id,
|
||||
this.canPopulate(populate, ['roles', 'avatar']) ? populate : ['roles'],
|
||||
);
|
||||
const doc = this.canPopulate(populate)
|
||||
? await this.userService.findOneAndPopulate(id)
|
||||
: await this.userService.findOne(id);
|
||||
|
||||
if (!doc) {
|
||||
this.logger.warn(`Unable to find User by id ${id}`);
|
||||
|
||||
@ -8,21 +8,26 @@
|
||||
*/
|
||||
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { TFilterQuery, Model } from 'mongoose';
|
||||
import { Model, TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import {
|
||||
Invitation,
|
||||
INVITATION_POPULATE,
|
||||
InvitationDocument,
|
||||
InvitationFull,
|
||||
InvitationPopulate,
|
||||
} from '../schemas/invitation.schema';
|
||||
import { hash } from '../utilities/hash';
|
||||
|
||||
export class InvitationRepository extends BaseRepository<Invitation, 'roles'> {
|
||||
export class InvitationRepository extends BaseRepository<
|
||||
Invitation,
|
||||
InvitationPopulate,
|
||||
InvitationFull
|
||||
> {
|
||||
constructor(@InjectModel(Invitation.name) readonly model: Model<Invitation>) {
|
||||
super(model, Invitation);
|
||||
super(model, Invitation, INVITATION_POPULATE, InvitationFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,32 +57,4 @@ export class InvitationRepository extends BaseRepository<Invitation, 'roles'> {
|
||||
: filter;
|
||||
return super.findQuery(filterWithHashedToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a paginated list of invitations based on the filter and page query and populates the `roles` field.
|
||||
*
|
||||
* @param filter - The filter object for querying invitations.
|
||||
* @param pageQuery - The pagination query parameters.
|
||||
*
|
||||
* @returns A promise that resolves to a list of populated `InvitationFull` objects.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<Invitation>,
|
||||
pageQuery: PageQueryDto<Invitation>,
|
||||
): Promise<InvitationFull[]> {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate('roles');
|
||||
return this.execute(query, InvitationFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single invitation by its ID and populates the `roles` field.
|
||||
*
|
||||
* @param {string} id - The ID of the invitation to be retrieved.
|
||||
*
|
||||
* @returns {Promise<InvitationFull>} - A promise that resolves to the populated `InvitationFull` object.
|
||||
*/
|
||||
async findOneAndPopulate(id: string): Promise<InvitationFull> {
|
||||
const query = this.findOneQuery(id).populate('roles');
|
||||
return this.executeOne(query, InvitationFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import { Model as ModelType } from './../schemas/model.schema';
|
||||
import { ModelRepository } from '../repositories/model.repository';
|
||||
import { PermissionRepository } from '../repositories/permission.repository';
|
||||
import { ModelModel } from '../schemas/model.schema';
|
||||
import { PermissionModel, Permission } from '../schemas/permission.schema';
|
||||
import { Permission, PermissionModel } from '../schemas/permission.schema';
|
||||
|
||||
describe('ModelRepository', () => {
|
||||
let modelRepository: ModelRepository;
|
||||
@ -69,7 +69,7 @@ describe('ModelRepository', () => {
|
||||
jest.spyOn(modelModel, 'find');
|
||||
const allModels = await modelRepository.findAll();
|
||||
const allPermissions = await permissionRepository.findAll();
|
||||
const result = await modelRepository.findAndPopulate({}, ['permissions']);
|
||||
const result = await modelRepository.findAndPopulate({});
|
||||
const modelsWithPermissions = allModels.reduce((acc, currModel) => {
|
||||
acc.push({
|
||||
...currModel,
|
||||
|
||||
@ -9,21 +9,30 @@
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { TFilterQuery, Model as MongooseModel } from 'mongoose';
|
||||
import { Model as MongooseModel } from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
|
||||
import { ModelFull, Model } from '../schemas/model.schema';
|
||||
import {
|
||||
Model,
|
||||
MODEL_POPULATE,
|
||||
ModelFull,
|
||||
ModelPopulate,
|
||||
} from '../schemas/model.schema';
|
||||
import { Permission } from '../schemas/permission.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ModelRepository extends BaseRepository<Model, 'permissions'> {
|
||||
export class ModelRepository extends BaseRepository<
|
||||
Model,
|
||||
ModelPopulate,
|
||||
ModelFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Model.name) readonly model: MongooseModel<Model>,
|
||||
@InjectModel(Permission.name)
|
||||
private readonly permissionModel: MongooseModel<Permission>,
|
||||
) {
|
||||
super(model, Model);
|
||||
super(model, Model, MODEL_POPULATE, ModelFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,32 +49,4 @@ export class ModelRepository extends BaseRepository<Model, 'permissions'> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds `Model` documents that match the provided filter and populates specified fields.
|
||||
*
|
||||
* @param filter - The filter query to apply.
|
||||
* @param populate - The fields to populate.
|
||||
*
|
||||
* @returns The populated `Model` documents.
|
||||
*/
|
||||
async findAndPopulate(
|
||||
filter: TFilterQuery<Model>,
|
||||
populate: string[],
|
||||
): Promise<ModelFull[]> {
|
||||
const query = this.findQuery(filter).populate(populate);
|
||||
return await this.execute(query, ModelFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single `Model` document by its ID and populates its `permissions` field.
|
||||
*
|
||||
* @param id - The ID of the `Model` document to find.
|
||||
*
|
||||
* @returns The populated `Model` document.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
const query = this.findOneQuery(id).populate('permissions');
|
||||
return await this.executeOne(query, ModelFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,18 +14,24 @@ import { Document, Model, Query, TFilterQuery, Types } from 'mongoose';
|
||||
|
||||
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
|
||||
import { Permission, PermissionFull } from '../schemas/permission.schema';
|
||||
import {
|
||||
Permission,
|
||||
PERMISSION_POPULATE,
|
||||
PermissionFull,
|
||||
PermissionPopulate,
|
||||
} from '../schemas/permission.schema';
|
||||
|
||||
@Injectable()
|
||||
export class PermissionRepository extends BaseRepository<
|
||||
Permission,
|
||||
'model' | 'role'
|
||||
PermissionPopulate,
|
||||
PermissionFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Permission.name) readonly model: Model<Permission>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Permission);
|
||||
super(model, Permission, PERMISSION_POPULATE, PermissionFull);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -16,12 +16,18 @@ import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { Permission } from '../schemas/permission.schema';
|
||||
import { Role, RoleFull } from '../schemas/role.schema';
|
||||
import {
|
||||
Role,
|
||||
ROLE_POPULATE,
|
||||
RoleFull,
|
||||
RolePopulate,
|
||||
} from '../schemas/role.schema';
|
||||
|
||||
@Injectable()
|
||||
export class RoleRepository extends BaseRepository<
|
||||
Role,
|
||||
'permissions' | 'users'
|
||||
RolePopulate,
|
||||
RoleFull
|
||||
> {
|
||||
constructor(
|
||||
@InjectModel(Role.name) readonly model: Model<Role>,
|
||||
@ -29,7 +35,7 @@ export class RoleRepository extends BaseRepository<
|
||||
private readonly permissionModel: Model<Permission>,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super(model, Role);
|
||||
super(model, Role, ROLE_POPULATE, RoleFull);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -13,6 +13,7 @@ import { MongooseModule, getModelToken } from '@nestjs/mongoose';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Model } from 'mongoose';
|
||||
|
||||
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { IGNORED_TEST_FIELDS } from '@/utils/test/constants';
|
||||
import { installPermissionFixtures } from '@/utils/test/fixtures/permission';
|
||||
@ -53,7 +54,12 @@ describe('UserRepository', () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [
|
||||
rootMongooseTestModule(installPermissionFixtures),
|
||||
MongooseModule.forFeature([UserModel, PermissionModel, RoleModel]),
|
||||
MongooseModule.forFeature([
|
||||
UserModel,
|
||||
PermissionModel,
|
||||
RoleModel,
|
||||
AttachmentModel,
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
LoggerService,
|
||||
@ -85,9 +91,7 @@ describe('UserRepository', () => {
|
||||
describe('findOneAndPopulate', () => {
|
||||
it('should find one user and populate its role', async () => {
|
||||
jest.spyOn(userModel, 'findById');
|
||||
const result = await userRepository.findOneAndPopulate(user.id, [
|
||||
'roles',
|
||||
]);
|
||||
const result = await userRepository.findOneAndPopulate(user.id);
|
||||
expect(userModel.findById).toHaveBeenCalledWith(user.id);
|
||||
expect(result).toEqualPayload(
|
||||
{
|
||||
@ -106,9 +110,7 @@ describe('UserRepository', () => {
|
||||
jest.spyOn(userRepository, 'findPageAndPopulate');
|
||||
const allUsers = await userRepository.findAll();
|
||||
const allRoles = await roleRepository.findAll();
|
||||
const result = await userRepository.findPageAndPopulate({}, pageQuery, [
|
||||
'roles',
|
||||
]);
|
||||
const result = await userRepository.findPageAndPopulate({}, pageQuery);
|
||||
const usersWithRoles = allUsers.reduce((acc, currUser) => {
|
||||
acc.push({
|
||||
...currUser,
|
||||
|
||||
@ -10,25 +10,34 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import {
|
||||
TFilterQuery,
|
||||
Document,
|
||||
Model,
|
||||
Query,
|
||||
TFilterQuery,
|
||||
UpdateQuery,
|
||||
UpdateWithAggregationPipeline,
|
||||
Document,
|
||||
} from 'mongoose';
|
||||
|
||||
import { BaseRepository } from '@/utils/generics/base-repository';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { UserEditProfileDto } from '../dto/user.dto';
|
||||
import { User, UserDocument, UserFull } from '../schemas/user.schema';
|
||||
import {
|
||||
User,
|
||||
USER_POPULATE,
|
||||
UserDocument,
|
||||
UserFull,
|
||||
UserPopulate,
|
||||
} from '../schemas/user.schema';
|
||||
import { hash } from '../utilities/bcryptjs';
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository extends BaseRepository<User, 'roles' | 'avatar'> {
|
||||
export class UserRepository extends BaseRepository<
|
||||
User,
|
||||
UserPopulate,
|
||||
UserFull
|
||||
> {
|
||||
constructor(@InjectModel(User.name) readonly model: Model<User>) {
|
||||
super(model, User);
|
||||
super(model, User, USER_POPULATE, UserFull);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,35 +96,4 @@ export class UserRepository extends BaseRepository<User, 'roles' | 'avatar'> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a page of user documents with population based on the given filter and page query.
|
||||
*
|
||||
* @param filter The filter criteria for finding users.
|
||||
* @param pageQuery The pagination and sorting information.
|
||||
* @param populate The fields to populate in the user documents.
|
||||
*
|
||||
* @returns A promise that resolves to an array of populated `UserFull` documents.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filter: TFilterQuery<User>,
|
||||
pageQuery: PageQueryDto<User>,
|
||||
populate: string[],
|
||||
): Promise<UserFull[]> {
|
||||
const query = this.findPageQuery(filter, pageQuery).populate(populate);
|
||||
return await this.execute(query, UserFull);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single user document by ID with population of related fields.
|
||||
*
|
||||
* @param id The ID of the user to find.
|
||||
* @param populate The fields to populate in the user document.
|
||||
*
|
||||
* @returns A promise that resolves to a populated `UserFull` document.
|
||||
*/
|
||||
async findOneAndPopulate(id: string, populate: string[]): Promise<UserFull> {
|
||||
const query = this.findOneQuery(id).populate(populate);
|
||||
return await this.executeOne(query, UserFull);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
import { isEmail } from '@/utils/validation-rules/is-email';
|
||||
|
||||
import { Role } from './role.schema';
|
||||
@ -70,3 +71,10 @@ export const InvitationModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default InvitationModel.schema;
|
||||
|
||||
export type InvitationPopulate = keyof TFilterPopulateFields<
|
||||
Invitation,
|
||||
InvitationStub
|
||||
>;
|
||||
|
||||
export const INVITATION_POPULATE: InvitationPopulate[] = ['roles'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Permission } from './permission.schema';
|
||||
import { TRelation } from '../types/index.type';
|
||||
@ -72,3 +73,7 @@ ModelModel.schema.virtual('permissions', {
|
||||
});
|
||||
|
||||
export default ModelModel.schema;
|
||||
|
||||
export type ModelPopulate = keyof TFilterPopulateFields<Model, ModelStub>;
|
||||
|
||||
export const MODEL_POPULATE: ModelPopulate[] = ['permissions'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Model } from './model.schema';
|
||||
import { Role } from './role.schema';
|
||||
@ -69,3 +70,10 @@ export const PermissionModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default PermissionModel.schema;
|
||||
|
||||
export type PermissionPopulate = keyof TFilterPopulateFields<
|
||||
Permission,
|
||||
PermissionStub
|
||||
>;
|
||||
|
||||
export const PERMISSION_POPULATE: PermissionPopulate[] = ['model', 'role'];
|
||||
|
||||
@ -13,6 +13,7 @@ import { THydratedDocument } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Permission } from './permission.schema';
|
||||
import { User } from './user.schema';
|
||||
@ -70,3 +71,7 @@ RoleModel.schema.virtual('users', {
|
||||
});
|
||||
|
||||
export default RoleModel.schema;
|
||||
|
||||
export type RolePopulate = keyof TFilterPopulateFields<Role, RoleStub>;
|
||||
|
||||
export const ROLE_POPULATE: RolePopulate[] = ['permissions', 'users'];
|
||||
|
||||
@ -7,13 +7,14 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
|
||||
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { THydratedDocument, Schema as MongooseSchema } from 'mongoose';
|
||||
import { Schema as MongooseSchema, THydratedDocument } from 'mongoose';
|
||||
|
||||
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||
import { BaseSchema } from '@/utils/generics/base-schema';
|
||||
import { LifecycleHookManager } from '@/utils/generics/lifecycle-hook-manager';
|
||||
import { TFilterPopulateFields } from '@/utils/types/filter.types';
|
||||
|
||||
import { Role } from './role.schema';
|
||||
import { UserProvider } from '../types/user-provider.type';
|
||||
@ -129,3 +130,7 @@ export const UserModel: ModelDefinition = LifecycleHookManager.attach({
|
||||
});
|
||||
|
||||
export default UserModel.schema;
|
||||
|
||||
export type UserPopulate = keyof TFilterPopulateFields<User, UserStub>;
|
||||
|
||||
export const USER_POPULATE: UserPopulate[] = ['roles', 'avatar'];
|
||||
|
||||
@ -12,10 +12,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { ModelRepository } from '../repositories/model.repository';
|
||||
import { Model } from '../schemas/model.schema';
|
||||
import { Model, ModelFull, ModelPopulate } from '../schemas/model.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ModelSeeder extends BaseSeeder<Model> {
|
||||
export class ModelSeeder extends BaseSeeder<Model, ModelPopulate, ModelFull> {
|
||||
constructor(private readonly modelRepository: ModelRepository) {
|
||||
super(modelRepository);
|
||||
}
|
||||
|
||||
@ -12,10 +12,18 @@ import { Injectable } from '@nestjs/common';
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { PermissionRepository } from '../repositories/permission.repository';
|
||||
import { Permission } from '../schemas/permission.schema';
|
||||
import {
|
||||
Permission,
|
||||
PermissionFull,
|
||||
PermissionPopulate,
|
||||
} from '../schemas/permission.schema';
|
||||
|
||||
@Injectable()
|
||||
export class PermissionSeeder extends BaseSeeder<Permission> {
|
||||
export class PermissionSeeder extends BaseSeeder<
|
||||
Permission,
|
||||
PermissionPopulate,
|
||||
PermissionFull
|
||||
> {
|
||||
constructor(private readonly permissionRepository: PermissionRepository) {
|
||||
super(permissionRepository);
|
||||
}
|
||||
|
||||
@ -12,10 +12,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { RoleRepository } from '../repositories/role.repository';
|
||||
import { Role } from '../schemas/role.schema';
|
||||
import { Role, RoleFull, RolePopulate } from '../schemas/role.schema';
|
||||
|
||||
@Injectable()
|
||||
export class RoleSeeder extends BaseSeeder<Role> {
|
||||
export class RoleSeeder extends BaseSeeder<Role, RolePopulate, RoleFull> {
|
||||
constructor(private readonly roleRepository: RoleRepository) {
|
||||
super(roleRepository);
|
||||
}
|
||||
|
||||
@ -12,10 +12,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { BaseSeeder } from '@/utils/generics/base-seeder';
|
||||
|
||||
import { UserRepository } from '../repositories/user.repository';
|
||||
import { User } from '../schemas/user.schema';
|
||||
import { User, UserFull, UserPopulate } from '../schemas/user.schema';
|
||||
|
||||
@Injectable()
|
||||
export class UserSeeder extends BaseSeeder<User> {
|
||||
export class UserSeeder extends BaseSeeder<User, UserPopulate, UserFull> {
|
||||
constructor(private readonly userRepository: UserRepository) {
|
||||
super(userRepository);
|
||||
}
|
||||
|
||||
@ -15,20 +15,26 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtService, JwtSignOptions } from '@nestjs/jwt';
|
||||
import { MailerService } from '@nestjs-modules/mailer';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { config } from '@/config';
|
||||
import { ExtendedI18nService } from '@/extended-i18n.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { InvitationCreateDto } from '../dto/invitation.dto';
|
||||
import { InvitationRepository } from '../repositories/invitation.repository';
|
||||
import { Invitation } from '../schemas/invitation.schema';
|
||||
import {
|
||||
Invitation,
|
||||
InvitationFull,
|
||||
InvitationPopulate,
|
||||
} from '../schemas/invitation.schema';
|
||||
|
||||
@Injectable()
|
||||
export class InvitationService extends BaseService<Invitation> {
|
||||
export class InvitationService extends BaseService<
|
||||
Invitation,
|
||||
InvitationPopulate,
|
||||
InvitationFull
|
||||
> {
|
||||
constructor(
|
||||
@Inject(InvitationRepository)
|
||||
readonly repository: InvitationRepository,
|
||||
@ -112,30 +118,4 @@ export class InvitationService extends BaseService<Invitation> {
|
||||
async updateOne(..._: any): Promise<Invitation> {
|
||||
throw new Error('Illegal Update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single invitation by ID and populates related data.
|
||||
*
|
||||
* @param id - The ID of the invitation to find.
|
||||
*
|
||||
* @returns The invitation with populated fields.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and paginates invitations based on the provided filters and pagination query, with populated related data.
|
||||
*
|
||||
* @param filters - The filters to apply when finding invitations.
|
||||
* @param pageQuery - The pagination query to apply.
|
||||
*
|
||||
* @returns A list of paginated invitations with populated fields.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<Invitation>,
|
||||
pageQuery: PageQueryDto<Invitation>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ describe('ModelService', () => {
|
||||
jest.spyOn(modelRepository, 'findAndPopulate');
|
||||
const models = await modelRepository.findAll();
|
||||
const permissions = await permissionRepository.findAll();
|
||||
const result = await modelService.findAndPopulate({}, ['permissions']);
|
||||
const result = await modelService.findAndPopulate({});
|
||||
const modelsWithPermissions = models.reduce((acc, currModel) => {
|
||||
acc.push({
|
||||
...currModel,
|
||||
@ -80,9 +80,10 @@ describe('ModelService', () => {
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
expect(modelRepository.findAndPopulate).toHaveBeenCalledWith({}, [
|
||||
'permissions',
|
||||
]);
|
||||
expect(modelRepository.findAndPopulate).toHaveBeenCalledWith(
|
||||
{},
|
||||
undefined,
|
||||
);
|
||||
expect(result).toEqualPayload(modelsWithPermissions);
|
||||
});
|
||||
});
|
||||
|
||||
@ -8,15 +8,14 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { ModelRepository } from '../repositories/model.repository';
|
||||
import { Model } from '../schemas/model.schema';
|
||||
import { Model, ModelFull, ModelPopulate } from '../schemas/model.schema';
|
||||
|
||||
@Injectable()
|
||||
export class ModelService extends BaseService<Model> {
|
||||
export class ModelService extends BaseService<Model, ModelPopulate, ModelFull> {
|
||||
constructor(readonly repository: ModelRepository) {
|
||||
super(repository);
|
||||
}
|
||||
@ -31,26 +30,4 @@ export class ModelService extends BaseService<Model> {
|
||||
async deleteOne(id: string) {
|
||||
return await this.repository.deleteOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds multiple Model entities based on provided filters and populates related entities.
|
||||
*
|
||||
* @param filters - The filters used to query the Model entities.
|
||||
* @param populate - Optional array of related entity fields to populate in the result.
|
||||
*
|
||||
* @returns A promise that resolves to the list of found Model entities with populated fields.
|
||||
*/
|
||||
async findAndPopulate(filters: TFilterQuery<Model>, populate?: string[]) {
|
||||
return await this.repository.findAndPopulate(filters, populate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a single Model entity by its unique identifier and populates related entities.
|
||||
*
|
||||
* @param id - The unique identifier of the Model entity to find.
|
||||
* @returns A promise that resolves to the found Model entity with populated fields.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,58 +11,32 @@ import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Cache } from 'cache-manager';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { PERMISSION_CACHE_KEY } from '@/utils/constants/cache';
|
||||
import { Cacheable } from '@/utils/decorators/cacheable.decorator';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
|
||||
import { PermissionRepository } from '../repositories/permission.repository';
|
||||
import { Permission, PermissionFull } from '../schemas/permission.schema';
|
||||
import {
|
||||
Permission,
|
||||
PermissionFull,
|
||||
PermissionPopulate,
|
||||
} from '../schemas/permission.schema';
|
||||
import { PermissionsTree } from '../types/permission.type';
|
||||
|
||||
@Injectable()
|
||||
export class PermissionService extends BaseService<Permission> {
|
||||
export class PermissionService extends BaseService<
|
||||
Permission,
|
||||
PermissionPopulate,
|
||||
PermissionFull
|
||||
> {
|
||||
constructor(
|
||||
readonly repository: PermissionRepository,
|
||||
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
|
||||
private readonly logger: LoggerService,
|
||||
) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all permissions and populates related fields such as roles and models.
|
||||
*
|
||||
* @returns A promise that resolves with the populated permissions.
|
||||
*/
|
||||
async findAllAndPopulate() {
|
||||
return await this.repository.findAllAndPopulate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves permissions based on the provided filter and populates related fields.
|
||||
*
|
||||
* @param filter - Filter criteria to apply when searching for permissions.
|
||||
*
|
||||
* @returns A promise that resolves with the filtered and populated permissions.
|
||||
*/
|
||||
async findAndPopulate(filter: TFilterQuery<Permission>) {
|
||||
return await this.repository.findAndPopulate(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single permission by its identifier and populates related fields.
|
||||
*
|
||||
* @param id - Identifier of the permission to retrieve.
|
||||
*
|
||||
* @returns A promise that resolves with the populated permission.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles permission update events by clearing the permissions cache.
|
||||
*
|
||||
|
||||
@ -8,43 +8,15 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { RoleRepository } from '../repositories/role.repository';
|
||||
import { Role } from '../schemas/role.schema';
|
||||
import { Role, RoleFull, RolePopulate } from '../schemas/role.schema';
|
||||
|
||||
@Injectable()
|
||||
export class RoleService extends BaseService<Role> {
|
||||
export class RoleService extends BaseService<Role, RolePopulate, RoleFull> {
|
||||
constructor(readonly repository: RoleRepository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a paginated list of roles with related fields populated, based on the provided filters and pagination options.
|
||||
*
|
||||
* @param filters - Criteria used to filter the roles.
|
||||
* @param pageQuery - Pagination options, including page size and number.
|
||||
*
|
||||
* @returns A paginated result set of roles with related fields populated.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<Role>,
|
||||
pageQuery: PageQueryDto<Role>,
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single role by its ID and populates its related fields.
|
||||
*
|
||||
* @param id - The unique identifier of the role to retrieve.
|
||||
*
|
||||
* @returns The role with related fields populated, or null if no role is found.
|
||||
*/
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,10 +99,8 @@ describe('UserService', () => {
|
||||
describe('findOneAndPopulate', () => {
|
||||
it('should find one user and populate its role', async () => {
|
||||
jest.spyOn(userRepository, 'findOneAndPopulate');
|
||||
const result = await userService.findOneAndPopulate(user.id, ['roles']);
|
||||
expect(userRepository.findOneAndPopulate).toHaveBeenCalledWith(user.id, [
|
||||
'roles',
|
||||
]);
|
||||
const result = await userService.findOneAndPopulate(user.id);
|
||||
expect(userRepository.findOneAndPopulate).toHaveBeenCalledWith(user.id);
|
||||
expect(result).toEqualPayload(
|
||||
{
|
||||
...userFixtures.find(({ username }) => username === 'admin'),
|
||||
@ -118,9 +116,7 @@ describe('UserService', () => {
|
||||
const pageQuery = getPageQuery<User>({ sort: ['_id', 'asc'] });
|
||||
jest.spyOn(userRepository, 'findPageAndPopulate');
|
||||
const allUsers = await userRepository.findAll();
|
||||
const result = await userService.findPageAndPopulate({}, pageQuery, [
|
||||
'roles',
|
||||
]);
|
||||
const result = await userService.findPageAndPopulate({}, pageQuery);
|
||||
const usersWithRoles = allUsers.reduce((acc, currUser) => {
|
||||
acc.push({
|
||||
...currUser,
|
||||
@ -132,7 +128,6 @@ describe('UserService', () => {
|
||||
expect(userRepository.findPageAndPopulate).toHaveBeenCalledWith(
|
||||
{},
|
||||
pageQuery,
|
||||
['roles'],
|
||||
);
|
||||
expect(result).toEqualPayload(usersWithRoles);
|
||||
});
|
||||
|
||||
@ -10,40 +10,20 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { Injectable, NotFoundException, StreamableFile } from '@nestjs/common';
|
||||
import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { getStreamableFile } from '@/attachment/utilities';
|
||||
import { config } from '@/config';
|
||||
import { BaseService } from '@/utils/generics/base-service';
|
||||
import { PageQueryDto } from '@/utils/pagination/pagination-query.dto';
|
||||
|
||||
import { RoleService } from './role.service';
|
||||
import { UserRepository } from '../repositories/user.repository';
|
||||
import { User } from '../schemas/user.schema';
|
||||
import { User, UserFull, UserPopulate } from '../schemas/user.schema';
|
||||
|
||||
@Injectable()
|
||||
export class UserService extends BaseService<User> {
|
||||
constructor(
|
||||
readonly repository: UserRepository,
|
||||
private readonly roleService: RoleService,
|
||||
private readonly attachmentService: AttachmentService,
|
||||
) {
|
||||
export class UserService extends BaseService<User, UserPopulate, UserFull> {
|
||||
constructor(readonly repository: UserRepository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a user by ID and populates the specified related fields.
|
||||
*
|
||||
* @param id - The ID of the user to find.
|
||||
* @param populate - (Optional) Array of related fields to populate in the result.
|
||||
*
|
||||
* @returns A promise that resolves with the populated user record.
|
||||
*/
|
||||
async findOneAndPopulate(id: string, populate?: string[]) {
|
||||
return await this.repository.findOneAndPopulate(id, populate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the user's profile picture as a streamable file.
|
||||
*
|
||||
@ -52,7 +32,7 @@ export class UserService extends BaseService<User> {
|
||||
* @returns A promise that resolves with the streamable file of the user's profile picture.
|
||||
*/
|
||||
async userProfilePic(id: string): Promise<StreamableFile> {
|
||||
const user = await this.findOneAndPopulate(id, ['avatar']);
|
||||
const user = await this.findOneAndPopulate(id);
|
||||
if (user) {
|
||||
const attachment = user.avatar;
|
||||
const path = join(config.parameters.uploadDir, attachment.location);
|
||||
@ -72,25 +52,4 @@ export class UserService extends BaseService<User> {
|
||||
throw new NotFoundException('Profile Not found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a paginated list of users based on filters and populates the specified related fields.
|
||||
*
|
||||
* @param filters - Filters to apply to the user search.
|
||||
* @param pageQuery - Pagination and sorting information for the query.
|
||||
* @param populate - (Optional) Array of related fields to populate in the result.
|
||||
*
|
||||
* @returns A promise that resolves with a paginated list of users.
|
||||
*/
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<User>,
|
||||
pageQuery: PageQueryDto<User>,
|
||||
populate?: string[],
|
||||
) {
|
||||
return await this.repository.findPageAndPopulate(
|
||||
filters,
|
||||
pageQuery,
|
||||
populate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { LoggerService } from '@nestjs/common';
|
||||
import { Cache } from 'cache-manager';
|
||||
|
||||
export function Cacheable(cacheKey: string) {
|
||||
@ -20,7 +19,6 @@ export function Cacheable(cacheKey: string) {
|
||||
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
const cache: Cache = this.cacheManager;
|
||||
const logger: LoggerService = this.logger; // Access the logger from the instance
|
||||
|
||||
if (!cache) {
|
||||
throw new Error(
|
||||
@ -28,19 +26,15 @@ export function Cacheable(cacheKey: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!logger) {
|
||||
throw new Error('Cacheable() requires the the logger service.');
|
||||
}
|
||||
|
||||
// Try to get cached data
|
||||
try {
|
||||
const cachedResult = await cache.get(cacheKey);
|
||||
if (cachedResult) {
|
||||
logger.debug(`Cache hit for key: ${cacheKey}`);
|
||||
return cachedResult;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Cache get error for key: ${cacheKey}:`, error);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Cache get error for key: ${cacheKey}:`, error);
|
||||
}
|
||||
|
||||
// Call the original method if cache miss
|
||||
@ -49,9 +43,9 @@ export function Cacheable(cacheKey: string) {
|
||||
// Set the new result in cache
|
||||
try {
|
||||
await cache.set(cacheKey, result);
|
||||
logger.debug(`Cache set for key: ${cacheKey}`);
|
||||
} catch (error) {
|
||||
logger.error(`Cache set error for key: ${cacheKey}:`, error);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Cache set error for key: ${cacheKey}:`, error);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@ -12,10 +12,15 @@ import { TFilterQuery } from 'mongoose';
|
||||
|
||||
import { BaseSchema } from './base-schema';
|
||||
import { BaseService } from './base-service';
|
||||
import { TValidateProps, TFilterPopulateFields } from '../types/filter.types';
|
||||
import { TValidateProps } from '../types/filter.types';
|
||||
|
||||
export abstract class BaseController<T extends BaseSchema, TStub = never> {
|
||||
constructor(private readonly service: BaseService<T>) {}
|
||||
export abstract class BaseController<
|
||||
T extends BaseSchema,
|
||||
TStub = never,
|
||||
P extends string = never,
|
||||
TFull extends Omit<T, P> = never,
|
||||
> {
|
||||
constructor(protected readonly service: BaseService<T, P, TFull>) {}
|
||||
|
||||
/**
|
||||
* Checks if the given populate fields are allowed based on the allowed fields list.
|
||||
@ -23,12 +28,12 @@ export abstract class BaseController<T extends BaseSchema, TStub = never> {
|
||||
* @param allowedFields - The list of allowed populate fields.
|
||||
* @return - True if all populate fields are allowed, otherwise false.
|
||||
*/
|
||||
protected canPopulate(
|
||||
populate: string[],
|
||||
allowedFields: (keyof TFilterPopulateFields<T, TStub>)[],
|
||||
): boolean {
|
||||
return (populate as typeof allowedFields).some((p) =>
|
||||
allowedFields.includes(p),
|
||||
protected canPopulate(populate: string[]): boolean {
|
||||
return populate.some((p) =>
|
||||
this.service
|
||||
.getRepository()
|
||||
.getPopulate()
|
||||
.includes(p as P),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ export type DeleteResult = {
|
||||
export abstract class BaseRepository<
|
||||
T extends FlattenMaps<unknown>,
|
||||
P extends string = never,
|
||||
TFull extends Omit<T, P> = never,
|
||||
U = Omit<T, keyof BaseSchema>,
|
||||
D = Document<T>,
|
||||
> {
|
||||
@ -42,10 +43,16 @@ export abstract class BaseRepository<
|
||||
constructor(
|
||||
readonly model: Model<T>,
|
||||
private readonly cls: new () => T,
|
||||
protected readonly populate: P[] = [],
|
||||
protected readonly clsPopulate: new () => TFull = undefined,
|
||||
) {
|
||||
this.registerLifeCycleHooks();
|
||||
}
|
||||
|
||||
getPopulate() {
|
||||
return this.populate;
|
||||
}
|
||||
|
||||
private registerLifeCycleHooks() {
|
||||
const repository = this;
|
||||
const hooks = LifecycleHookManager.getHooks(this.cls.name);
|
||||
@ -153,6 +160,12 @@ export abstract class BaseRepository<
|
||||
return await this.executeOne(query, this.cls, options);
|
||||
}
|
||||
|
||||
async findOneAndPopulate(criteria: string | TFilterQuery<T>) {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findOneQuery(criteria).populate(this.populate);
|
||||
return await this.executeOne(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
protected findQuery(filter: TFilterQuery<T>, sort?: QuerySortDto<T>) {
|
||||
const query = this.model.find<T>(filter);
|
||||
if (sort) {
|
||||
@ -166,14 +179,32 @@ export abstract class BaseRepository<
|
||||
return await this.execute(query, this.cls);
|
||||
}
|
||||
|
||||
protected findAllQuery() {
|
||||
return this.findQuery({});
|
||||
private ensureCanPopulate() {
|
||||
if (!this.populate || !this.clsPopulate) {
|
||||
throw new Error('Cannot populate query');
|
||||
}
|
||||
}
|
||||
|
||||
async findAndPopulate(filters: TFilterQuery<T>, sort?: QuerySortDto<T>) {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findQuery(filters, sort).populate(this.populate);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
protected findAllQuery(sort?: QuerySortDto<T>) {
|
||||
return this.findQuery({}, sort);
|
||||
}
|
||||
|
||||
async findAll(sort?: QuerySortDto<T>) {
|
||||
return await this.find({}, sort);
|
||||
}
|
||||
|
||||
async findAllAndPopulate(sort?: QuerySortDto<T>) {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findAllQuery(sort).populate(this.populate);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
protected findPageQuery(
|
||||
filters: TFilterQuery<T>,
|
||||
{ skip, limit, sort }: PageQueryDto<T>,
|
||||
@ -192,6 +223,17 @@ export abstract class BaseRepository<
|
||||
return await this.execute(query, this.cls);
|
||||
}
|
||||
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQuery: PageQueryDto<T>,
|
||||
) {
|
||||
this.ensureCanPopulate();
|
||||
const query = this.findPageQuery(filters, pageQuery).populate(
|
||||
this.populate,
|
||||
);
|
||||
return await this.execute(query, this.clsPopulate);
|
||||
}
|
||||
|
||||
async countAll(): Promise<number> {
|
||||
return await this.model.estimatedDocumentCount().exec();
|
||||
}
|
||||
|
||||
@ -10,8 +10,12 @@
|
||||
import { BaseRepository } from './base-repository';
|
||||
import { BaseSchema } from './base-schema';
|
||||
|
||||
export abstract class BaseSeeder<T, P extends string = never> {
|
||||
constructor(protected readonly repository: BaseRepository<T, P>) {}
|
||||
export abstract class BaseSeeder<
|
||||
T,
|
||||
P extends string = never,
|
||||
TFull extends Omit<T, P> = never,
|
||||
> {
|
||||
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
|
||||
|
||||
async findAll(): Promise<T[]> {
|
||||
return await this.repository.findAll();
|
||||
|
||||
@ -16,8 +16,16 @@ import { BaseRepository } from './base-repository';
|
||||
import { BaseSchema } from './base-schema';
|
||||
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
|
||||
|
||||
export abstract class BaseService<T extends BaseSchema> {
|
||||
constructor(readonly repository: BaseRepository<T, never>) {}
|
||||
export abstract class BaseService<
|
||||
T extends BaseSchema,
|
||||
P extends string = never,
|
||||
TFull extends Omit<T, P> = never,
|
||||
> {
|
||||
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
|
||||
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async findOne(
|
||||
criteria: string | TFilterQuery<T>,
|
||||
@ -26,14 +34,26 @@ export abstract class BaseService<T extends BaseSchema> {
|
||||
return await this.repository.findOne(criteria, options);
|
||||
}
|
||||
|
||||
async findOneAndPopulate(id: string) {
|
||||
return await this.repository.findOneAndPopulate(id);
|
||||
}
|
||||
|
||||
async find(filter: TFilterQuery<T>, sort?: QuerySortDto<T>): Promise<T[]> {
|
||||
return await this.repository.find(filter, sort);
|
||||
}
|
||||
|
||||
async findAndPopulate(filters: TFilterQuery<T>, sort?: QuerySortDto<T>) {
|
||||
return await this.repository.findAndPopulate(filters, sort);
|
||||
}
|
||||
|
||||
async findAll(sort?: QuerySortDto<T>): Promise<T[]> {
|
||||
return await this.repository.findAll(sort);
|
||||
}
|
||||
|
||||
async findAllAndPopulate(sort?: QuerySortDto<T>): Promise<TFull[]> {
|
||||
return await this.repository.findAllAndPopulate(sort);
|
||||
}
|
||||
|
||||
async findPage(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQueryDto: PageQueryDto<T>,
|
||||
@ -41,6 +61,13 @@ export abstract class BaseService<T extends BaseSchema> {
|
||||
return await this.repository.findPage(filters, pageQueryDto);
|
||||
}
|
||||
|
||||
async findPageAndPopulate(
|
||||
filters: TFilterQuery<T>,
|
||||
pageQueryDto: PageQueryDto<T>,
|
||||
): Promise<TFull[]> {
|
||||
return await this.repository.findPageAndPopulate(filters, pageQueryDto);
|
||||
}
|
||||
|
||||
async countAll(): Promise<number> {
|
||||
return await this.repository.countAll();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user