Merge branch '548-issue-generic-support-of-multiple-dto-per-service' into 540-issue-nlp-module-strictnullchecks-issues

This commit is contained in:
yassinedorbozgithub
2025-01-13 14:14:40 +01:00
88 changed files with 608 additions and 263 deletions

View File

@@ -161,7 +161,6 @@ export class ChannelService {
foreign_id: req.session.passport.user.id,
},
{
id: req.session.passport.user.id,
foreign_id: req.session.passport.user.id,
first_name: req.session.passport.user.first_name,
last_name: req.session.passport.user.last_name,

View File

@@ -21,6 +21,7 @@ import {
IsString,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { CaptureVar } from '../schemas/types/capture-var';
@@ -146,3 +147,7 @@ export class BlockUpdateDto extends PartialType(
@IsOptional()
trigger_channels?: string[];
}
export type BlockDto = DtoConfig<{
create: BlockCreateDto;
}>;

View File

@@ -8,14 +8,16 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import {
IsArray,
IsBoolean,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsNumber,
IsArray,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export class CategoryCreateDto {
@ApiProperty({ description: 'Category label', type: String })
@IsNotEmpty()
@@ -39,3 +41,7 @@ export class CategoryCreateDto {
}
export class CategoryUpdateDto extends PartialType(CategoryCreateDto) {}
export type CategoryDto = DtoConfig<{
create: CategoryCreateDto;
}>;

View File

@@ -9,6 +9,8 @@
import { ApiProperty, PartialType } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export class ContextVarCreateDto {
@ApiProperty({ description: 'Context var label', type: String })
@IsNotEmpty()
@@ -27,3 +29,7 @@ export class ContextVarCreateDto {
}
export class ContextVarUpdateDto extends PartialType(ContextVarCreateDto) {}
export type ContextVarDto = DtoConfig<{
create: ContextVarCreateDto;
}>;

View File

@@ -16,6 +16,7 @@ import {
IsString,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { Context } from './../schemas/types/context';
@@ -45,7 +46,7 @@ export class ConversationCreateDto {
@IsObjectId({
message: 'Current must be a valid objectId',
})
current: string;
current?: string | null;
@ApiProperty({ description: 'next conversation', type: Array })
@IsOptional()
@@ -54,5 +55,9 @@ export class ConversationCreateDto {
each: true,
message: 'next must be a valid objectId',
})
next: string[];
next?: string[];
}
export type ConversationDto = DtoConfig<{
create: ConversationCreateDto;
}>;

View File

@@ -9,12 +9,14 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import {
IsNotEmpty,
IsObject,
IsOptional,
IsString,
Matches,
IsObject,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export class LabelCreateDto {
@ApiProperty({ description: 'Label title', type: String })
@IsNotEmpty()
@@ -39,3 +41,7 @@ export class LabelCreateDto {
}
export class LabelUpdateDto extends PartialType(LabelCreateDto) {}
export type LabelDto = DtoConfig<{
create: LabelCreateDto;
}>;

View File

@@ -17,6 +17,7 @@ import {
} from 'class-validator';
import { ChannelName } from '@/channel/types';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { SubscriberChannelData } from '../schemas/types/channel';
@@ -36,7 +37,7 @@ export class SubscriberCreateDto {
@ApiPropertyOptional({ description: 'Subscriber locale', type: String })
@IsOptional()
@IsString()
locale: string;
locale?: string;
@ApiPropertyOptional({ description: 'Subscriber timezone', type: Number })
@IsOptional()
@@ -51,17 +52,17 @@ export class SubscriberCreateDto {
@ApiPropertyOptional({ description: 'Subscriber gender', type: String })
@IsOptional()
@IsString()
gender: string;
gender?: string;
@ApiPropertyOptional({ description: 'Subscriber country', type: String })
@IsOptional()
@IsString()
country: string;
country?: string;
@ApiPropertyOptional({ description: 'Subscriber foreign id', type: String })
@IsOptional()
@IsString()
foreign_id: string;
foreign_id?: string;
@ApiProperty({ description: 'Subscriber labels', type: Array })
@IsNotEmpty()
@@ -114,3 +115,7 @@ export class SubscriberCreateDto {
}
export class SubscriberUpdateDto extends PartialType(SubscriberCreateDto) {}
export type SubscriberDto = DtoConfig<{
create: SubscriberCreateDto;
}>;

View File

@@ -22,7 +22,7 @@ import { LoggerService } from '@/logger/logger.service';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { BlockCreateDto, BlockUpdateDto } from '../dto/block.dto';
import { BlockCreateDto, BlockDto, BlockUpdateDto } from '../dto/block.dto';
import {
Block,
BLOCK_POPULATE,
@@ -34,7 +34,8 @@ import {
export class BlockRepository extends BaseRepository<
Block,
BlockPopulate,
BlockFull
BlockFull,
BlockDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -14,11 +14,17 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { CategoryDto } from '../dto/category.dto';
import { Category } from '../schemas/category.schema';
import { BlockService } from '../services/block.service';
@Injectable()
export class CategoryRepository extends BaseRepository<Category> {
export class CategoryRepository extends BaseRepository<
Category,
never,
never,
CategoryDto
> {
private readonly blockService: BlockService;
constructor(

View File

@@ -19,11 +19,17 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { ContextVarDto } from '../dto/context-var.dto';
import { ContextVar } from '../schemas/context-var.schema';
import { BlockService } from '../services/block.service';
@Injectable()
export class ContextVarRepository extends BaseRepository<ContextVar> {
export class ContextVarRepository extends BaseRepository<
ContextVar,
never,
never,
ContextVarDto
> {
private readonly blockService: BlockService;
constructor(

View File

@@ -13,6 +13,7 @@ import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import { ConversationDto } from '../dto/conversation.dto';
import {
Conversation,
CONVERSATION_POPULATE,
@@ -24,7 +25,8 @@ import {
export class ConversationRepository extends BaseRepository<
Conversation,
ConversationPopulate,
ConversationFull
ConversationFull,
ConversationDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { LabelDto } from '../dto/label.dto';
import {
Label,
LABEL_POPULATE,
@@ -26,7 +27,8 @@ import {
export class LabelRepository extends BaseRepository<
Label,
LabelPopulate,
LabelFull
LabelFull,
LabelDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -20,7 +20,7 @@ import {
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { SubscriberUpdateDto } from '../dto/subscriber.dto';
import { SubscriberDto, SubscriberUpdateDto } from '../dto/subscriber.dto';
import {
Subscriber,
SUBSCRIBER_POPULATE,
@@ -33,7 +33,8 @@ import {
export class SubscriberRepository extends BaseRepository<
Subscriber,
SubscriberPopulate,
SubscriberFull
SubscriberFull,
SubscriberDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -43,7 +43,7 @@ export class BlockStub extends BaseSchema {
validate: isPatternList,
default: [],
})
patterns?: Pattern[];
patterns: Pattern[];
@Prop([
{
@@ -52,7 +52,7 @@ export class BlockStub extends BaseSchema {
default: [],
},
])
trigger_labels?: unknown;
trigger_labels: unknown;
@Prop([
{
@@ -61,19 +61,19 @@ export class BlockStub extends BaseSchema {
default: [],
},
])
assign_labels?: unknown;
assign_labels: unknown;
@Prop({
type: Object,
default: [],
})
trigger_channels?: string[];
trigger_channels: string[];
@Prop({
type: Object,
default: {},
})
options?: BlockOptions;
options: BlockOptions;
@Prop({
type: Object,
@@ -88,13 +88,13 @@ export class BlockStub extends BaseSchema {
default: [],
},
])
nextBlocks?: unknown;
nextBlocks: unknown;
@Prop({
type: MongooseSchema.Types.ObjectId,
ref: 'Block',
})
attachedBlock?: unknown;
attachedBlock: unknown;
@Prop({
type: MongooseSchema.Types.ObjectId,
@@ -106,14 +106,14 @@ export class BlockStub extends BaseSchema {
type: Boolean,
default: false,
})
starts_conversation?: boolean;
starts_conversation: boolean;
@Prop({
type: Object,
validate: isValidVarCapture,
default: [],
})
capture_vars?: CaptureVar[];
capture_vars: CaptureVar[];
@Prop({
type: Object,
@@ -125,22 +125,22 @@ export class BlockStub extends BaseSchema {
type: Boolean,
default: false,
})
builtin?: boolean;
builtin: boolean;
}
@Schema({ timestamps: true })
export class Block extends BlockStub {
@Transform(({ obj }) => obj.trigger_labels?.map((elem) => elem.toString()))
trigger_labels?: string[];
@Transform(({ obj }) => obj.trigger_labels.map((elem) => elem.toString()))
trigger_labels: string[];
@Transform(({ obj }) => obj.assign_labels?.map((elem) => elem.toString()))
assign_labels?: string[];
@Transform(({ obj }) => obj.assign_labels.map((elem) => elem.toString()))
assign_labels: string[];
@Transform(({ obj }) => obj.nextBlocks?.map((elem) => elem.toString()))
nextBlocks?: string[];
@Transform(({ obj }) => obj.nextBlocks.map((elem) => elem.toString()))
nextBlocks: string[];
@Transform(({ obj }) => obj.attachedBlock?.toString() || null)
attachedBlock?: string;
attachedBlock: string;
@Transform(({ obj }) => obj.category.toString())
category: string;
@@ -161,16 +161,16 @@ export class BlockFull extends BlockStub {
assign_labels: Label[];
@Type(() => Block)
nextBlocks?: Block[];
nextBlocks: Block[];
@Type(() => Block)
attachedBlock?: Block;
attachedBlock: Block;
@Type(() => Category)
category: Category;
@Type(() => Block)
previousBlocks: Block[];
previousBlocks?: Block[];
@Type(() => Block)
attachedToBlock?: Block;

View File

@@ -25,19 +25,19 @@ export class Category extends BaseSchema {
type: Boolean,
default: false,
})
builtin?: boolean;
builtin: boolean;
@Prop({
type: Number,
default: 100,
})
zoom?: number;
zoom: number;
@Prop({
type: [Number, Number],
default: [0, 0],
})
offset?: [number, number];
offset: [number, number];
}
export const CategoryModel: ModelDefinition = LifecycleHookManager.attach({

View File

@@ -38,7 +38,7 @@ export class ContextVar extends BaseSchema {
type: Boolean,
default: false,
})
permanent?: boolean;
permanent: boolean;
}
export const ContextVarModel: ModelDefinition = LifecycleHookManager.attach({

View File

@@ -51,19 +51,19 @@ class ConversationStub extends BaseSchema {
type: Boolean,
default: true,
})
active?: boolean;
active: boolean;
@Prop({
type: Object,
default: getDefaultConversationContext(),
})
context?: Context;
context: Context;
@Prop({
type: MongooseSchema.Types.ObjectId,
ref: 'Block',
})
current?: unknown;
current: unknown;
@Prop([
{
@@ -72,7 +72,7 @@ class ConversationStub extends BaseSchema {
default: [],
},
])
next?: unknown;
next: unknown;
}
@Schema({ timestamps: true })
@@ -80,11 +80,11 @@ export class Conversation extends ConversationStub {
@Transform(({ obj }) => obj.sender.toString())
sender: string;
@Transform(({ obj }) => obj.current.toString())
current?: string;
@Transform(({ obj }) => (obj.current ? obj.current.toString() : null))
current: string | null;
@Transform(({ obj }) => obj.next.map((elem) => elem.toString()))
next?: string[];
next: string[];
}
@Schema({ timestamps: true })

View File

@@ -43,13 +43,13 @@ export class LabelStub extends BaseSchema {
@Prop({
type: String,
})
description?: string;
description: string;
@Prop({
type: Boolean,
default: false,
})
builtin?: boolean;
builtin: boolean;
}
@Schema({ timestamps: true })

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { CategoryDto } from '../dto/category.dto';
import { CategoryRepository } from '../repositories/category.repository';
import { Category } from '../schemas/category.schema';
@Injectable()
export class CategorySeeder extends BaseSeeder<Category> {
export class CategorySeeder extends BaseSeeder<
Category,
never,
never,
CategoryDto
> {
constructor(private readonly categoryRepository: CategoryRepository) {
super(categoryRepository);
}

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { ContextVarDto } from '../dto/context-var.dto';
import { ContextVarRepository } from '../repositories/context-var.repository';
import { ContextVar } from '../schemas/context-var.schema';
@Injectable()
export class ContextVarSeeder extends BaseSeeder<ContextVar> {
export class ContextVarSeeder extends BaseSeeder<
ContextVar,
never,
never,
ContextVarDto
> {
constructor(private readonly contextVarRepository: ContextVarRepository) {
super(contextVarRepository);
}

View File

@@ -22,6 +22,7 @@ import { SettingService } from '@/setting/services/setting.service';
import { BaseService } from '@/utils/generics/base-service';
import { getRandom } from '@/utils/helpers/safeRandom';
import { BlockDto } from '../dto/block.dto';
import { BlockRepository } from '../repositories/block.repository';
import { Block, BlockFull, BlockPopulate } from '../schemas/block.schema';
import { Context } from '../schemas/types/context';
@@ -35,7 +36,12 @@ import { Payload, StdQuickReply } from '../schemas/types/quick-reply';
import { SubscriberContext } from '../schemas/types/subscriberContext';
@Injectable()
export class BlockService extends BaseService<Block, BlockPopulate, BlockFull> {
export class BlockService extends BaseService<
Block,
BlockPopulate,
BlockFull,
BlockDto
> {
constructor(
readonly repository: BlockRepository,
private readonly contentService: ContentService,

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { CategoryDto } from '../dto/category.dto';
import { CategoryRepository } from '../repositories/category.repository';
import { Category } from '../schemas/category.schema';
@Injectable()
export class CategoryService extends BaseService<Category> {
export class CategoryService extends BaseService<
Category,
never,
never,
CategoryDto
> {
constructor(readonly repository: CategoryRepository) {
super(repository);
}

View File

@@ -10,12 +10,18 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { ContextVarDto } from '../dto/context-var.dto';
import { ContextVarRepository } from '../repositories/context-var.repository';
import { Block, BlockFull } from '../schemas/block.schema';
import { ContextVar } from '../schemas/context-var.schema';
@Injectable()
export class ContextVarService extends BaseService<ContextVar> {
export class ContextVarService extends BaseService<
ContextVar,
never,
never,
ContextVarDto
> {
constructor(readonly repository: ContextVarRepository) {
super(repository);
}

View File

@@ -12,6 +12,7 @@ import EventWrapper from '@/channel/lib/EventWrapper';
import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service';
import { ConversationDto } from '../dto/conversation.dto';
import { VIEW_MORE_PAYLOAD } from '../helpers/constants';
import { ConversationRepository } from '../repositories/conversation.repository';
import { Block, BlockFull } from '../schemas/block.schema';
@@ -30,7 +31,8 @@ import { SubscriberService } from './subscriber.service';
export class ConversationService extends BaseService<
Conversation,
ConversationPopulate,
ConversationFull
ConversationFull,
ConversationDto
> {
constructor(
readonly repository: ConversationRepository,

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { LabelDto } from '../dto/label.dto';
import { LabelRepository } from '../repositories/label.repository';
import { Label, LabelFull, LabelPopulate } from '../schemas/label.schema';
@Injectable()
export class LabelService extends BaseService<Label, LabelPopulate, LabelFull> {
export class LabelService extends BaseService<
Label,
LabelPopulate,
LabelFull,
LabelDto
> {
constructor(readonly repository: LabelRepository) {
super(repository);
}

View File

@@ -28,7 +28,7 @@ import { SocketRequest } from '@/websocket/utils/socket-request';
import { SocketResponse } from '@/websocket/utils/socket-response';
import { WebsocketGateway } from '@/websocket/websocket.gateway';
import { SubscriberUpdateDto } from '../dto/subscriber.dto';
import { SubscriberDto, SubscriberUpdateDto } from '../dto/subscriber.dto';
import { SubscriberRepository } from '../repositories/subscriber.repository';
import {
Subscriber,
@@ -40,7 +40,8 @@ import {
export class SubscriberService extends BaseService<
Subscriber,
SubscriberPopulate,
SubscriberFull
SubscriberFull,
SubscriberDto
> {
private readonly gateway: WebsocketGateway;

View File

@@ -9,6 +9,7 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
export class ContentCreateDto {
@@ -34,3 +35,7 @@ export class ContentCreateDto {
}
export class ContentUpdateDto extends PartialType(ContentCreateDto) {}
export type ContentDto = DtoConfig<{
create: ContentCreateDto;
}>;

View File

@@ -9,16 +9,18 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsString,
IsArray,
ValidateNested,
IsOptional,
IsEnum,
Matches,
IsNotEmpty,
IsOptional,
IsString,
Matches,
Validate,
ValidateNested,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { ValidateRequiredFields } from '../validators/validate-required-fields.validator';
export class FieldType {
@@ -55,3 +57,7 @@ export class ContentTypeCreateDto {
}
export class ContentTypeUpdateDto extends PartialType(ContentTypeCreateDto) {}
export type ContentTypeDto = DtoConfig<{
create: ContentTypeCreateDto;
}>;

View File

@@ -16,6 +16,7 @@ import {
ValidateIf,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { MenuType } from '../schemas/types/menu';
@@ -53,3 +54,7 @@ export class MenuCreateDto {
}
export class MenuQueryDto extends PartialType(MenuCreateDto) {}
export type MenuDto = DtoConfig<{
create: MenuCreateDto;
}>;

View File

@@ -15,11 +15,17 @@ import { BlockService } from '@/chat/services/block.service';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { ContentTypeDto } from '../dto/contentType.dto';
import { ContentType } from '../schemas/content-type.schema';
import { Content } from '../schemas/content.schema';
@Injectable()
export class ContentTypeRepository extends BaseRepository<ContentType> {
export class ContentTypeRepository extends BaseRepository<
ContentType,
never,
never,
ContentTypeDto
> {
constructor(
readonly eventEmitter: EventEmitter2,
@InjectModel(ContentType.name) readonly model: Model<ContentType>,

View File

@@ -21,6 +21,7 @@ import {
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { ContentDto } from '../dto/content.dto';
import {
Content,
CONTENT_POPULATE,
@@ -32,7 +33,8 @@ import {
export class ContentRepository extends BaseRepository<
Content,
ContentPopulate,
ContentFull
ContentFull,
ContentDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -13,6 +13,7 @@ import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import { MenuDto } from '../dto/menu.dto';
import {
Menu,
MENU_POPULATE,
@@ -26,7 +27,8 @@ import { MenuType } from '../schemas/types/menu';
export class MenuRepository extends BaseRepository<
Menu,
MenuPopulate,
MenuFull
MenuFull,
MenuDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -6,7 +6,7 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { Prop, Schema, SchemaFactory, ModelDefinition } from '@nestjs/mongoose';
import { ModelDefinition, Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import mongoose from 'mongoose';
import { BaseSchema } from '@/utils/generics/base-schema';
@@ -39,7 +39,7 @@ export class ContentType extends BaseSchema {
},
],
})
fields?: {
fields: {
name: string;
label: string;
type: string;

View File

@@ -41,7 +41,7 @@ export class ContentStub extends BaseSchema {
* Either of not this content is active.
*/
@Prop({ type: Boolean, default: true })
status?: boolean;
status: boolean;
@Prop({ type: mongoose.Schema.Types.Mixed })
dynamicFields: Record<string, any>;

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { ContentTypeDto } from '../dto/contentType.dto';
import { ContentTypeRepository } from '../repositories/content-type.repository';
import { ContentType } from '../schemas/content-type.schema';
@Injectable()
export class ContentTypeService extends BaseService<ContentType> {
export class ContentTypeService extends BaseService<
ContentType,
never,
never,
ContentTypeDto
> {
constructor(readonly repository: ContentTypeRepository) {
super(repository);
}

View File

@@ -19,6 +19,7 @@ import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service';
import { TFilterQuery } from '@/utils/types/filter.types';
import { ContentDto } from '../dto/content.dto';
import { ContentRepository } from '../repositories/content.repository';
import {
Content,
@@ -30,7 +31,8 @@ import {
export class ContentService extends BaseService<
Content,
ContentPopulate,
ContentFull
ContentFull,
ContentDto
> {
constructor(
readonly repository: ContentRepository,

View File

@@ -20,13 +20,18 @@ 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 { MenuCreateDto, MenuDto } from '../dto/menu.dto';
import { MenuRepository } from '../repositories/menu.repository';
import { Menu, MenuFull, MenuPopulate } from '../schemas/menu.schema';
import { AnyMenu, MenuTree, MenuType } from '../schemas/types/menu';
@Injectable()
export class MenuService extends BaseService<Menu, MenuPopulate, MenuFull> {
export class MenuService extends BaseService<
Menu,
MenuPopulate,
MenuFull,
MenuDto
> {
private RootSymbol: symbol = Symbol('RootMenu');
constructor(

View File

@@ -137,12 +137,16 @@ describe('BaseNlpHelper', () => {
name: 'intent',
createdAt: new Date(),
updatedAt: new Date(),
builtin: false,
lookups: [],
},
entity2: {
id: new ObjectId().toString(),
name: 'test-entity',
createdAt: new Date(),
updatedAt: new Date(),
builtin: false,
lookups: [],
},
});
jest.spyOn(NlpValue, 'getValueMap').mockReturnValue({
@@ -152,6 +156,9 @@ describe('BaseNlpHelper', () => {
entity: 'entity1', // Add the required entity field
createdAt: new Date(),
updatedAt: new Date(),
builtin: false,
expressions: [],
metadata: [],
},
value2: {
id: new ObjectId().toString(),
@@ -159,6 +166,9 @@ describe('BaseNlpHelper', () => {
entity: 'entity2', // Add the required entity field
createdAt: new Date(),
updatedAt: new Date(),
builtin: false,
expressions: [],
metadata: [],
},
});
@@ -195,6 +205,8 @@ describe('BaseNlpHelper', () => {
name: 'test-entity',
createdAt: new Date(),
updatedAt: new Date(),
builtin: false,
lookups: [],
},
});

View File

@@ -7,9 +7,11 @@
*/
import { PartialType } from '@nestjs/mapped-types';
import { ApiProperty } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export class LanguageCreateDto {
@ApiProperty({ description: 'Language Title', type: String })
@IsNotEmpty()
@@ -25,10 +27,14 @@ export class LanguageCreateDto {
@IsBoolean()
isRTL: boolean;
@ApiProperty({ description: 'Is Default Language ?', type: Boolean })
@ApiPropertyOptional({ description: 'Is Default Language ?', type: Boolean })
@IsOptional()
@IsBoolean()
isDefault?: boolean;
}
export class LanguageUpdateDto extends PartialType(LanguageCreateDto) {}
export type LanguageDto = DtoConfig<{
create: LanguageCreateDto;
}>;

View File

@@ -14,10 +14,16 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { LanguageDto } from '../dto/language.dto';
import { Language } from '../schemas/language.schema';
@Injectable()
export class LanguageRepository extends BaseRepository<Language> {
export class LanguageRepository extends BaseRepository<
Language,
never,
never,
LanguageDto
> {
constructor(
readonly eventEmitter: EventEmitter2,
@InjectModel(Language.name) readonly model: Model<Language>,

View File

@@ -32,13 +32,13 @@ export class Language extends BaseSchema {
type: Boolean,
default: false,
})
isDefault?: boolean;
isDefault: boolean;
@Prop({
type: Boolean,
default: false,
})
isRTL?: boolean;
isRTL: boolean;
}
export const LanguageModel: ModelDefinition = LifecycleHookManager.attach({

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { LanguageDto } from '../dto/language.dto';
import { LanguageRepository } from '../repositories/language.repository';
import { Language } from '../schemas/language.schema';
@Injectable()
export class LanguageSeeder extends BaseSeeder<Language> {
export class LanguageSeeder extends BaseSeeder<
Language,
never,
never,
LanguageDto
> {
constructor(private readonly languageRepository: LanguageRepository) {
super(languageRepository);
}

View File

@@ -23,11 +23,17 @@ import {
import { Cacheable } from '@/utils/decorators/cacheable.decorator';
import { BaseService } from '@/utils/generics/base-service';
import { LanguageDto } from '../dto/language.dto';
import { LanguageRepository } from '../repositories/language.repository';
import { Language } from '../schemas/language.schema';
@Injectable()
export class LanguageService extends BaseService<Language> {
export class LanguageService extends BaseService<
Language,
never,
never,
LanguageDto
> {
constructor(
readonly repository: LanguageRepository,
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache,

View File

@@ -154,6 +154,7 @@ describe('TranslationService', () => {
},
},
options: {},
attachedBlock: null,
};
const mockedPlugin: any = {

View File

@@ -8,15 +8,17 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsString,
IsOptional,
IsBoolean,
IsArray,
Matches,
IsBoolean,
IsIn,
IsNotEmpty,
IsOptional,
IsString,
Matches,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export type Lookup = 'keywords' | 'trait' | 'free-text';
export class NlpEntityCreateDto {
@@ -46,3 +48,7 @@ export class NlpEntityCreateDto {
@IsOptional()
builtin?: boolean;
}
export type NlpEntityDto = DtoConfig<{
create: NlpEntityCreateDto;
}>;

View File

@@ -15,6 +15,7 @@ import {
IsString,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { NlpSampleEntityValue, NlpSampleState } from '../schemas/types';
@@ -40,7 +41,7 @@ export class NlpSampleCreateDto {
@IsString()
@IsIn(Object.values(NlpSampleState))
@IsOptional()
type?: NlpSampleState;
type?: keyof typeof NlpSampleState;
@ApiProperty({ description: 'NLP sample language id', type: String })
@IsString()
@@ -63,3 +64,7 @@ export class NlpSampleDto extends NlpSampleCreateDto {
}
export class NlpSampleUpdateDto extends PartialType(NlpSampleCreateDto) {}
export type TNlpSampleDto = DtoConfig<{
create: NlpSampleCreateDto;
}>;

View File

@@ -9,14 +9,15 @@
import { PartialType } from '@nestjs/mapped-types';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsString,
IsBoolean,
IsArray,
IsObject,
IsBoolean,
IsNotEmpty,
IsObject,
IsOptional,
IsString,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
export class NlpValueCreateDto {
@@ -52,3 +53,7 @@ export class NlpValueCreateDto {
}
export class NlpValueUpdateDto extends PartialType(NlpValueCreateDto) {}
export type NlpValueDto = DtoConfig<{
create: NlpValueCreateDto;
}>;

View File

@@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { NlpEntityDto } from '../dto/nlp-entity.dto';
import {
NLP_ENTITY_POPULATE,
NlpEntity,
@@ -29,7 +30,8 @@ import { NlpValueRepository } from './nlp-value.repository';
export class NlpEntityRepository extends BaseRepository<
NlpEntity,
NlpEntityPopulate,
NlpEntityFull
NlpEntityFull,
NlpEntityDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { TNlpSampleDto } from '../dto/nlp-sample.dto';
import {
NLP_SAMPLE_POPULATE,
NlpSample,
@@ -27,7 +28,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
export class NlpSampleRepository extends BaseRepository<
NlpSample,
NlpSamplePopulate,
NlpSampleFull
NlpSampleFull,
TNlpSampleDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -14,6 +14,7 @@ import { Document, Model, Query } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { NlpValueDto } from '../dto/nlp-value.dto';
import {
NLP_VALUE_POPULATE,
NlpValue,
@@ -28,7 +29,8 @@ import { NlpSampleEntityRepository } from './nlp-sample-entity.repository';
export class NlpValueRepository extends BaseRepository<
NlpValue,
NlpValuePopulate,
NlpValueFull
NlpValueFull,
NlpValueDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -44,7 +44,7 @@ export class NlpEntityStub extends BaseSchema {
* Lookup strategy can contain : keywords, trait, free-text
*/
@Prop({ type: [String], default: ['keywords'] })
lookups?: Lookup[];
lookups: Lookup[];
/**
* Description of the entity purpose.
@@ -56,7 +56,7 @@ export class NlpEntityStub extends BaseSchema {
* Either or not this entity a built-in (either fixtures or shipped along with the 3rd party ai).
*/
@Prop({ type: Boolean, default: false })
builtin?: boolean;
builtin: boolean;
/**
* Returns a map object for entities

View File

@@ -33,7 +33,7 @@ export class NlpSampleStub extends BaseSchema {
* Either or not this sample was used for traning already.
*/
@Prop({ type: Boolean, default: false })
trained?: boolean;
trained: boolean;
/**
* From where this sample was provided.
@@ -43,7 +43,7 @@ export class NlpSampleStub extends BaseSchema {
enum: Object.values(NlpSampleState),
default: NlpSampleState.train,
})
type?: keyof typeof NlpSampleState;
type: keyof typeof NlpSampleState;
/**
* The language of the sample.

View File

@@ -38,19 +38,19 @@ export class NlpValueStub extends BaseSchema {
* An array of synonyms or equivalent words that fits this value.
*/
@Prop({ type: [String], default: [] })
expressions?: string[];
expressions: string[];
/**
* Metadata are additional data that can be associated to this values, most of the time, the metadata contains system values or ids (e.g: value: "coffee", metadata: "item_11") .
*/
@Prop({ type: JSON, default: {} })
metadata?: Record<string, any>;
metadata: Record<string, any>;
/**
* Either or not this value a built-in (either fixtures or shipped along with the 3rd party ai).
*/
@Prop({ type: Boolean, default: false })
builtin?: boolean;
builtin: boolean;
/**
* The entity to which this value belongs to.

View File

@@ -10,6 +10,7 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { NlpEntityDto } from '../dto/nlp-entity.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import {
NlpEntity,
@@ -21,7 +22,8 @@ import {
export class NlpEntitySeeder extends BaseSeeder<
NlpEntity,
NlpEntityPopulate,
NlpEntityFull
NlpEntityFull,
NlpEntityDto
> {
constructor(nlpEntityRepository: NlpEntityRepository) {
super(nlpEntityRepository);

View File

@@ -8,9 +8,9 @@
import { Injectable } from '@nestjs/common';
import { BaseSchema } from '@/utils/generics/base-schema';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { NlpValueCreateDto, NlpValueDto } from '../dto/nlp-value.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import {
@@ -23,7 +23,8 @@ import {
export class NlpValueSeeder extends BaseSeeder<
NlpValue,
NlpValuePopulate,
NlpValueFull
NlpValueFull,
NlpValueDto
> {
constructor(
nlpValueRepository: NlpValueRepository,
@@ -32,7 +33,7 @@ export class NlpValueSeeder extends BaseSeeder<
super(nlpValueRepository);
}
async seed(models: Omit<NlpValue, keyof BaseSchema>[]): Promise<boolean> {
async seed(models: NlpValueCreateDto[]): Promise<boolean> {
if (await this.isEmpty()) {
const entities = await this.nlpEntityRepository.findAll();
const modelDtos = models.map((v) => ({

View File

@@ -10,7 +10,7 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { Lookup } from '../dto/nlp-entity.dto';
import { Lookup, NlpEntityDto } from '../dto/nlp-entity.dto';
import { NlpEntityRepository } from '../repositories/nlp-entity.repository';
import {
NlpEntity,
@@ -25,7 +25,8 @@ import { NlpValueService } from './nlp-value.service';
export class NlpEntityService extends BaseService<
NlpEntity,
NlpEntityPopulate,
NlpEntityFull
NlpEntityFull,
NlpEntityDto
> {
constructor(
readonly repository: NlpEntityRepository,

View File

@@ -21,7 +21,7 @@ import { LoggerService } from '@/logger/logger.service';
import { BaseService } from '@/utils/generics/base-service';
import { THydratedDocument } from '@/utils/types/filter.types';
import { NlpSampleCreateDto } from '../dto/nlp-sample.dto';
import { NlpSampleCreateDto, TNlpSampleDto } from '../dto/nlp-sample.dto';
import { NlpSampleRepository } from '../repositories/nlp-sample.repository';
import {
NlpSample,
@@ -37,7 +37,8 @@ import { NlpSampleEntityService } from './nlp-sample-entity.service';
export class NlpSampleService extends BaseService<
NlpSample,
NlpSamplePopulate,
NlpSampleFull
NlpSampleFull,
TNlpSampleDto
> {
constructor(
readonly repository: NlpSampleRepository,

View File

@@ -11,7 +11,11 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { DeleteResult } from '@/utils/generics/base-repository';
import { BaseService } from '@/utils/generics/base-service';
import { NlpValueCreateDto, NlpValueUpdateDto } from '../dto/nlp-value.dto';
import {
NlpValueCreateDto,
NlpValueDto,
NlpValueUpdateDto,
} from '../dto/nlp-value.dto';
import { NlpValueRepository } from '../repositories/nlp-value.repository';
import { NlpEntity } from '../schemas/nlp-entity.schema';
import {
@@ -27,7 +31,8 @@ import { NlpEntityService } from './nlp-entity.service';
export class NlpValueService extends BaseService<
NlpValue,
NlpValuePopulate,
NlpValueFull
NlpValueFull,
NlpValueDto
> {
constructor(
readonly repository: NlpValueRepository,

View File

@@ -7,8 +7,9 @@
*/
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsString, IsOptional } from 'class-validator';
import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
import { Action } from '../types/action.type';
@@ -40,3 +41,7 @@ export class PermissionCreateDto {
@IsOptional()
relation?: TRelation;
}
export type PermissionDto = DtoConfig<{
create: PermissionCreateDto;
}>;

View File

@@ -9,6 +9,8 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
export class RoleCreateDto {
@ApiProperty({ description: 'Name of the role', type: String })
@IsNotEmpty()
@@ -26,3 +28,7 @@ export class RoleCreateDto {
}
export class RoleUpdateDto extends PartialType(RoleCreateDto) {}
export type RoleDto = DtoConfig<{
create: RoleCreateDto;
}>;

View File

@@ -22,6 +22,7 @@ import {
IsString,
} from 'class-validator';
import { DtoConfig } from '@/utils/types/dto.types';
import { IsObjectId } from '@/utils/validation-rules/is-object-id';
export class UserCreateDto {
@@ -61,12 +62,21 @@ export class UserCreateDto {
@IsString()
@IsObjectId({ message: 'Avatar must be a valid ObjectId' })
avatar: string | null = null;
@ApiPropertyOptional({
description: 'User state',
type: Boolean,
})
@IsOptional()
@IsBoolean()
state?: boolean;
}
export class UserEditProfileDto extends OmitType(PartialType(UserCreateDto), [
'username',
'roles',
'avatar',
'state',
]) {
@ApiPropertyOptional({ description: 'User language', type: String })
@IsOptional()
@@ -98,3 +108,7 @@ export class UserResetPasswordDto extends PickType(UserCreateDto, [
]) {}
export class UserRequestResetDto extends PickType(UserCreateDto, ['email']) {}
export type UserDto = DtoConfig<{
create: UserCreateDto;
}>;

View File

@@ -13,6 +13,7 @@ import { Model } from 'mongoose';
import { BaseRepository } from '@/utils/generics/base-repository';
import { PermissionDto } from '../dto/permission.dto';
import {
Permission,
PERMISSION_POPULATE,
@@ -24,7 +25,8 @@ import {
export class PermissionRepository extends BaseRepository<
Permission,
PermissionPopulate,
PermissionFull
PermissionFull,
PermissionDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -13,6 +13,7 @@ import { Model } from 'mongoose';
import { BaseRepository, DeleteResult } from '@/utils/generics/base-repository';
import { RoleDto } from '../dto/role.dto';
import { Permission } from '../schemas/permission.schema';
import {
Role,
@@ -25,7 +26,8 @@ import {
export class RoleRepository extends BaseRepository<
Role,
RolePopulate,
RoleFull
RoleFull,
RoleDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -20,7 +20,7 @@ import {
import { BaseRepository } from '@/utils/generics/base-repository';
import { TFilterQuery } from '@/utils/types/filter.types';
import { UserEditProfileDto } from '../dto/user.dto';
import { UserDto, UserEditProfileDto } from '../dto/user.dto';
import {
User,
USER_POPULATE,
@@ -34,7 +34,8 @@ import { hash } from '../utilities/bcryptjs';
export class UserRepository extends BaseRepository<
User,
UserPopulate,
UserFull
UserFull,
UserDto
> {
constructor(
readonly eventEmitter: EventEmitter2,

View File

@@ -18,6 +18,7 @@ import {
} from '@/utils/types/filter.types';
import { Action } from '../types/action.type';
import { TRelation } from '../types/index.type';
import { Model } from './model.schema';
import { Role } from './role.schema';
@@ -41,7 +42,7 @@ export class PermissionStub extends BaseSchema {
type: String,
default: 'role',
})
relation?: string;
relation: TRelation;
}
@Schema({ timestamps: true })

View File

@@ -31,7 +31,7 @@ export class RoleStub extends BaseSchema {
name: string;
@Prop({ type: Boolean, default: true })
active?: boolean;
active: boolean;
}
@Schema({ timestamps: true })

View File

@@ -50,6 +50,16 @@ export class UserStub extends BaseSchema {
})
password: string;
@Prop([{ type: MongooseSchema.Types.ObjectId, ref: 'Role' }])
roles: unknown;
@Prop({
type: MongooseSchema.Types.ObjectId,
ref: 'Attachment',
default: null,
})
avatar: unknown;
@Prop({
type: Boolean,
default: false,
@@ -83,16 +93,6 @@ export class UserStub extends BaseSchema {
})
resetCount?: number;
@Prop([{ type: MongooseSchema.Types.ObjectId, ref: 'Role' }])
roles: unknown;
@Prop({
type: MongooseSchema.Types.ObjectId,
ref: 'Attachment',
default: null,
})
avatar: unknown;
@Prop({
type: String,
default: null,

View File

@@ -10,6 +10,7 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { PermissionDto } from '../dto/permission.dto';
import { PermissionRepository } from '../repositories/permission.repository';
import {
Permission,
@@ -21,7 +22,8 @@ import {
export class PermissionSeeder extends BaseSeeder<
Permission,
PermissionPopulate,
PermissionFull
PermissionFull,
PermissionDto
> {
constructor(private readonly permissionRepository: PermissionRepository) {
super(permissionRepository);

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseSeeder } from '@/utils/generics/base-seeder';
import { RoleDto } from '../dto/role.dto';
import { RoleRepository } from '../repositories/role.repository';
import { Role, RoleFull, RolePopulate } from '../schemas/role.schema';
@Injectable()
export class RoleSeeder extends BaseSeeder<Role, RolePopulate, RoleFull> {
export class RoleSeeder extends BaseSeeder<
Role,
RolePopulate,
RoleFull,
RoleDto
> {
constructor(private readonly roleRepository: RoleRepository) {
super(roleRepository);
}

View File

@@ -15,6 +15,7 @@ import { PERMISSION_CACHE_KEY } from '@/utils/constants/cache';
import { Cacheable } from '@/utils/decorators/cacheable.decorator';
import { BaseService } from '@/utils/generics/base-service';
import { PermissionDto } from '../dto/permission.dto';
import { PermissionRepository } from '../repositories/permission.repository';
import {
Permission,
@@ -27,7 +28,8 @@ import { PermissionsTree } from '../types/permission.type';
export class PermissionService extends BaseService<
Permission,
PermissionPopulate,
PermissionFull
PermissionFull,
PermissionDto
> {
constructor(
readonly repository: PermissionRepository,

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { RoleDto } from '../dto/role.dto';
import { RoleRepository } from '../repositories/role.repository';
import { Role, RoleFull, RolePopulate } from '../schemas/role.schema';
@Injectable()
export class RoleService extends BaseService<Role, RolePopulate, RoleFull> {
export class RoleService extends BaseService<
Role,
RolePopulate,
RoleFull,
RoleDto
> {
constructor(readonly repository: RoleRepository) {
super(repository);
}

View File

@@ -10,11 +10,17 @@ import { Injectable } from '@nestjs/common';
import { BaseService } from '@/utils/generics/base-service';
import { UserDto } from '../dto/user.dto';
import { UserRepository } from '../repositories/user.repository';
import { User, UserFull, UserPopulate } from '../schemas/user.schema';
@Injectable()
export class UserService extends BaseService<User, UserPopulate, UserFull> {
export class UserService extends BaseService<
User,
UserPopulate,
UserFull,
UserDto
> {
constructor(readonly repository: UserRepository) {
super(repository);
}

View File

@@ -10,6 +10,7 @@ import { NotFoundException } from '@nestjs/common';
import { TFilterQuery } from '@/utils/types/filter.types';
import { DtoConfig } from '../types/dto.types';
import { TValidateProps } from '../types/filter.types';
import { BaseSchema } from './base-schema';
@@ -20,8 +21,9 @@ export abstract class BaseController<
TStub = never,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
> {
constructor(protected readonly service: BaseService<T, P, TFull>) {}
constructor(protected readonly service: BaseService<T, P, TFull, Dto>) {}
/**
* Checks if the given populate fields are allowed based on the allowed fields list.

View File

@@ -30,6 +30,7 @@ import {
import { TFilterQuery } from '@/utils/types/filter.types';
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseSchema } from './base-schema';
import { LifecycleHookManager } from './lifecycle-hook-manager';
@@ -70,7 +71,8 @@ export abstract class BaseRepository<
T extends FlattenMaps<unknown>,
P extends string = never,
TFull extends Omit<T, P> = never,
U = Omit<T, keyof BaseSchema>,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
D = Document<T>,
> {
private readonly transformOpts = { excludePrefixes: ['_', 'password'] };
@@ -454,7 +456,7 @@ export abstract class BaseRepository<
return await this.model.countDocuments(criteria).exec();
}
async create(dto: U): Promise<T> {
async create(dto: DtoInfer<DtoAction.Create, Dto, U>): Promise<T> {
const doc = await this.model.create(dto);
return plainToClass(
@@ -464,7 +466,9 @@ export abstract class BaseRepository<
);
}
async createMany(dtoArray: U[]): Promise<T[]> {
async createMany(
dtoArray: DtoInfer<DtoAction.Create, Dto, U>[],
): Promise<T[]> {
const docs = await this.model.create(dtoArray);
return docs.map((doc) =>

View File

@@ -8,6 +8,8 @@
import { FlattenMaps } from 'mongoose';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseRepository } from './base-repository';
import { BaseSchema } from './base-schema';
@@ -15,8 +17,12 @@ export abstract class BaseSeeder<
T extends FlattenMaps<unknown>,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
> {
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
constructor(
protected readonly repository: BaseRepository<T, P, TFull, Dto>,
) {}
async findAll(): Promise<T[]> {
return await this.repository.findAll();
@@ -27,7 +33,7 @@ export abstract class BaseSeeder<
return count === 0;
}
async seed(models: Omit<T, keyof BaseSchema>[]): Promise<boolean> {
async seed(models: DtoInfer<DtoAction.Create, Dto, U>[]): Promise<boolean> {
if (await this.isEmpty()) {
await this.repository.createMany(models);
return true;

View File

@@ -14,6 +14,7 @@ import { ProjectionType, QueryOptions } from 'mongoose';
import { TFilterQuery } from '@/utils/types/filter.types';
import { PageQueryDto, QuerySortDto } from '../pagination/pagination-query.dto';
import { DtoAction, DtoConfig, DtoInfer } from '../types/dto.types';
import { BaseRepository } from './base-repository';
import { BaseSchema } from './base-schema';
@@ -22,8 +23,12 @@ export abstract class BaseService<
T extends BaseSchema,
P extends string = never,
TFull extends Omit<T, P> = never,
Dto extends DtoConfig = object,
U extends Omit<T, keyof BaseSchema> = Omit<T, keyof BaseSchema>,
> {
constructor(protected readonly repository: BaseRepository<T, P, TFull>) {}
constructor(
protected readonly repository: BaseRepository<T, P, TFull, Dto>,
) {}
getRepository() {
return this.repository;
@@ -140,7 +145,7 @@ export abstract class BaseService<
return await this.repository.count(criteria);
}
async create<D extends Omit<T, keyof BaseSchema>>(dto: D): Promise<T> {
async create(dto: DtoInfer<DtoAction.Create, Dto, U>): Promise<T> {
try {
return await this.repository.create(dto);
} catch (error) {
@@ -153,9 +158,9 @@ export abstract class BaseService<
}
}
async findOneOrCreate<D extends Omit<T, keyof BaseSchema>>(
async findOneOrCreate(
criteria: string | TFilterQuery<T>,
dto: D,
dto: DtoInfer<DtoAction.Create, Dto, U>,
): Promise<T> {
const result = await this.findOne(criteria);
if (!result) {
@@ -164,24 +169,21 @@ export abstract class BaseService<
return result;
}
async createMany<D extends Omit<T, keyof BaseSchema>>(
dtoArray: D[],
async createMany(
dtoArray: DtoInfer<DtoAction.Create, Dto, U>[],
): Promise<T[]> {
return await this.repository.createMany(dtoArray);
}
async updateOne<D extends Partial<Omit<T, keyof BaseSchema>>>(
async updateOne(
criteria: string | TFilterQuery<T>,
dto: D,
options?: QueryOptions<D> | null,
dto: Partial<U>,
options?: QueryOptions<Partial<U>> | null,
): Promise<T | null> {
return await this.repository.updateOne(criteria, dto, options);
}
async updateMany<D extends Partial<Omit<T, keyof BaseSchema>>>(
filter: TFilterQuery<T>,
dto: D,
) {
async updateMany(filter: TFilterQuery<T>, dto: Partial<U>) {
return await this.repository.updateMany(filter, dto);
}

View File

@@ -9,20 +9,34 @@
import mongoose from 'mongoose';
import { BlockCreateDto } from '@/chat/dto/block.dto';
import { BlockModel, Block } from '@/chat/schemas/block.schema';
import { Block, BlockModel } from '@/chat/schemas/block.schema';
import { CategoryModel } from '@/chat/schemas/category.schema';
import { FileType } from '@/chat/schemas/types/attachment';
import { ButtonType } from '@/chat/schemas/types/button';
import { QuickReplyType } from '@/chat/schemas/types/quick-reply';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const blocks: BlockCreateDto[] = [
type TBlockFixtures = FixturesTypeBuilder<Block, BlockCreateDto>;
export const blockDefaultValues: TBlockFixtures['defaultValues'] = {
options: {},
nextBlocks: [],
capture_vars: [],
assign_labels: [],
trigger_labels: [],
trigger_channels: [],
builtin: false,
starts_conversation: false,
attachedBlock: null,
attachedToBlock: null,
};
export const blocks: TBlockFixtures['values'][] = [
{
name: 'hasNextBlocks',
patterns: ['Hi'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -41,7 +55,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'hasPreviousBlocks',
patterns: ['colors'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -79,7 +92,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'buttons',
patterns: ['about'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -117,7 +129,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'attachment',
patterns: ['image'],
trigger_channels: [],
category: null,
options: {
typing: 0,
@@ -144,7 +155,6 @@ export const blocks: BlockCreateDto[] = [
{
name: 'test',
patterns: ['yes'],
trigger_channels: [],
category: null,
//to be verified
options: {
@@ -163,18 +173,9 @@ export const blocks: BlockCreateDto[] = [
},
];
export const blockDefaultValues: TFixturesDefaultValues<Block> = {
options: {},
builtin: false,
nextBlocks: [],
capture_vars: [],
assign_labels: [],
trigger_labels: [],
starts_conversation: false,
attachedToBlock: null,
};
export const blockFixtures = getFixturesWithDefaultValues<Block>({
export const blockFixtures = getFixturesWithDefaultValues<
TBlockFixtures['values']
>({
fixtures: blocks,
defaultValues: blockDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -9,12 +9,23 @@
import mongoose from 'mongoose';
import { CategoryCreateDto } from '@/chat/dto/category.dto';
import { CategoryModel, Category } from '@/chat/schemas/category.schema';
import { Category, CategoryModel } from '@/chat/schemas/category.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const categories: CategoryCreateDto[] = [
export type TCategoryFixtures = FixturesTypeBuilder<
Category,
CategoryCreateDto
>;
export const categoryDefaultValues: TCategoryFixtures['defaultValues'] = {
builtin: false,
zoom: 100,
offset: [0, 0],
};
export const categories: TCategoryFixtures['values'][] = [
{
label: 'test category 1',
},
@@ -23,13 +34,9 @@ export const categories: CategoryCreateDto[] = [
},
];
export const categoryDefaultValues: TFixturesDefaultValues<Category> = {
builtin: false,
zoom: 100,
offset: [0, 0],
};
export const categoryFixtures = getFixturesWithDefaultValues<Category>({
export const categoryFixtures = getFixturesWithDefaultValues<
TCategoryFixtures['values']
>({
fixtures: categories,
defaultValues: categoryDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,12 +12,18 @@ import { ContentCreateDto } from '@/cms/dto/content.dto';
import { Content, ContentModel } from '@/cms/schemas/content.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installAttachmentFixtures } from './attachment';
import { installContentTypeFixtures } from './contenttype';
const contents: ContentCreateDto[] = [
type TContentFixtures = FixturesTypeBuilder<Content, ContentCreateDto>;
export const contentDefaultValues: TContentFixtures['defaultValues'] = {
status: true,
};
const contents: TContentFixtures['values'][] = [
{
title: 'Jean',
dynamicFields: {
@@ -131,14 +137,11 @@ const contents: ContentCreateDto[] = [
},
];
export const categoryDefaultValues: TFixturesDefaultValues<Content> = {
status: true,
createdAt: undefined,
};
export const contentFixtures = getFixturesWithDefaultValues<Content>({
export const contentFixtures = getFixturesWithDefaultValues<
TContentFixtures['values']
>({
fixtures: contents,
defaultValues: categoryDefaultValues,
defaultValues: contentDefaultValues,
});
export const installContentFixtures = async () => {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -15,9 +15,29 @@ import {
} from '@/cms/schemas/content-type.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
const contentTypes: ContentTypeCreateDto[] = [
type TContentTypeFixtures = FixturesTypeBuilder<
ContentType,
ContentTypeCreateDto
>;
export const contentTypeDefaultValues: TContentTypeFixtures['defaultValues'] = {
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
},
{
name: 'status',
label: 'Status',
type: 'checkbox',
},
],
};
const contentTypes: TContentTypeFixtures['values'][] = [
{
name: 'Product',
fields: [
@@ -100,22 +120,9 @@ const contentTypes: ContentTypeCreateDto[] = [
},
];
export const contentTypeDefaultValues: TFixturesDefaultValues<ContentType> = {
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
},
{
name: 'status',
label: 'Status',
type: 'checkbox',
},
],
};
export const contentTypeFixtures = getFixturesWithDefaultValues<ContentType>({
export const contentTypeFixtures = getFixturesWithDefaultValues<
TContentTypeFixtures['values']
>({
fixtures: contentTypes,
defaultValues: contentTypeDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -9,25 +9,33 @@
import mongoose from 'mongoose';
import { ContextVarCreateDto } from '@/chat/dto/context-var.dto';
import { ContextVarModel, ContextVar } from '@/chat/schemas/context-var.schema';
import { ContextVar, ContextVarModel } from '@/chat/schemas/context-var.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { FixturesTypeBuilder } from '../types';
const contextVars: ContextVarCreateDto[] = [
type TContentVarFixtures = FixturesTypeBuilder<ContextVar, ContextVarCreateDto>;
export const contentVarDefaultValues: TContentVarFixtures['defaultValues'] = {
permanent: false,
};
const contextVars: TContentVarFixtures['values'][] = [
{
label: 'test context var 1',
name: 'test1',
permanent: false,
},
{
label: 'test context var 2',
name: 'test2',
permanent: false,
},
];
export const contextVarFixtures = getFixturesWithDefaultValues<ContextVar>({
export const contextVarFixtures = getFixturesWithDefaultValues<
TContentVarFixtures['values']
>({
fixtures: contextVars,
defaultValues: contentVarDefaultValues,
});
export const installContextVarFixtures = async () => {

View File

@@ -9,10 +9,7 @@
import mongoose from 'mongoose';
import { ConversationCreateDto } from '@/chat/dto/conversation.dto';
import {
Conversation,
ConversationModel,
} from '@/chat/schemas/conversation.schema';
import { ConversationModel } from '@/chat/schemas/conversation.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
@@ -116,14 +113,16 @@ const conversations: ConversationCreateDto[] = [
},
];
export const conversationDefaultValues: TFixturesDefaultValues<Conversation> = {
active: false,
};
export const conversationDefaultValues: TFixturesDefaultValues<ConversationCreateDto> =
{
active: false,
};
export const conversationFixtures = getFixturesWithDefaultValues<Conversation>({
fixtures: conversations,
defaultValues: conversationDefaultValues,
});
export const conversationFixtures =
getFixturesWithDefaultValues<ConversationCreateDto>({
fixtures: conversations,
defaultValues: conversationDefaultValues,
});
export const installConversationTypeFixtures = async () => {
const subscribers = await installSubscriberFixtures();

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,9 +12,15 @@ import { LabelCreateDto } from '@/chat/dto/label.dto';
import { Label, LabelModel } from '@/chat/schemas/label.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
export const labels: LabelCreateDto[] = [
type TLabelFixtures = FixturesTypeBuilder<Label, LabelCreateDto>;
export const contentLabelDefaultValues: TLabelFixtures['defaultValues'] = {
builtin: false,
};
export const labels: TLabelFixtures['values'][] = [
{
description: 'test description 1',
label_id: {
@@ -39,13 +45,11 @@ export const labels: LabelCreateDto[] = [
},
];
export const labelDefaultValues: TFixturesDefaultValues<Label> = {
builtin: false,
};
export const labelFixtures = getFixturesWithDefaultValues<Label>({
export const labelFixtures = getFixturesWithDefaultValues<
TLabelFixtures['values']
>({
fixtures: labels,
defaultValues: labelDefaultValues,
defaultValues: contentLabelDefaultValues,
});
export const installLabelFixtures = async () => {

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -13,11 +13,18 @@ import { NlpSample, NlpSampleModel } from '@/nlp/schemas/nlp-sample.schema';
import { NlpSampleState } from '@/nlp/schemas/types';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installLanguageFixtures } from './language';
const nlpSamples: NlpSampleCreateDto[] = [
type TNlpSampleFixtures = FixturesTypeBuilder<NlpSample, NlpSampleCreateDto>;
export const nlpSampleDefaultValues: TNlpSampleFixtures['defaultValues'] = {
type: NlpSampleState.train,
trained: false,
};
const nlpSamples: TNlpSampleFixtures['values'][] = [
{
text: 'yess',
language: '0',
@@ -38,12 +45,9 @@ const nlpSamples: NlpSampleCreateDto[] = [
},
];
export const nlpSampleDefaultValues: TFixturesDefaultValues<NlpSample> = {
type: NlpSampleState.train,
trained: false,
};
export const nlpSampleFixtures = getFixturesWithDefaultValues<NlpSample>({
export const nlpSampleFixtures = getFixturesWithDefaultValues<
TNlpSampleFixtures['values']
>({
fixtures: nlpSamples,
defaultValues: nlpSampleDefaultValues,
});

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -12,12 +12,23 @@ import { SubscriberCreateDto } from '@/chat/dto/subscriber.dto';
import { Subscriber, SubscriberModel } from '@/chat/schemas/subscriber.schema';
import { getFixturesWithDefaultValues } from '../defaultValues';
import { TFixturesDefaultValues } from '../types';
import { FixturesTypeBuilder } from '../types';
import { installLabelFixtures } from './label';
import { installUserFixtures } from './user';
const subscribers: SubscriberCreateDto[] = [
type TSubscriberFixtures = FixturesTypeBuilder<Subscriber, SubscriberCreateDto>;
export const subscriberDefaultValues: TSubscriberFixtures['defaultValues'] = {
timezone: 0,
assignedTo: null,
assignedAt: null,
lastvisit: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
retainedFrom: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
avatar: null,
};
const subscribers: TSubscriberFixtures['values'][] = [
{
foreign_id: 'foreign-id-messenger',
first_name: 'Jhon',
@@ -30,7 +41,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'messenger-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2020-01-01T20:40:03.249Z'),
retainedFrom: new Date('2020-01-01T20:40:03.249Z'),
},
@@ -46,7 +56,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2021-01-01T20:40:03.249Z'),
retainedFrom: new Date('2021-01-02T20:40:03.249Z'),
},
@@ -62,7 +71,6 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2022-01-01T20:40:03.249Z'),
retainedFrom: new Date('2022-01-02T20:40:03.249Z'),
},
@@ -78,22 +86,14 @@ const subscribers: SubscriberCreateDto[] = [
name: 'web-channel',
},
labels: [],
assignedAt: null,
lastvisit: new Date('2024-01-01T20:40:03.249Z'),
retainedFrom: new Date('2024-01-02T20:40:03.249Z'),
},
];
export const subscriberDefaultValues: TFixturesDefaultValues<Subscriber> = {
timezone: 0,
assignedTo: null,
assignedAt: null,
lastvisit: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
retainedFrom: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
avatar: null,
};
export const subscriberFixtures = getFixturesWithDefaultValues<Subscriber>({
export const subscriberFixtures = getFixturesWithDefaultValues<
TSubscriberFixtures['values']
>({
fixtures: subscribers,
defaultValues: subscriberDefaultValues,
});

View File

@@ -95,6 +95,7 @@ export const baseBlockInstance = {
category: undefined,
previousBlocks: [],
trigger_channels: [],
nextBlocks: [],
...modelInstance,
};
@@ -103,6 +104,7 @@ export const blockEmpty: BlockFull = {
name: 'Empty',
patterns: [],
message: [''],
nextBlocks: [],
};
// Translation Data

View File

@@ -8,7 +8,7 @@
import { BaseSchema } from '../generics/base-schema';
type TOptionalPropertyOf<T> = Exclude<
export type TOptionalPropertyOf<T> = Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? never : K;
}[keyof T],
@@ -23,3 +23,25 @@ export type TFixtures<T> = Omit<T, keyof BaseSchema> & {
export type TFixturesDefaultValues<T, S = TFixtures<T>> = {
[key in TOptionalPropertyOf<S>]?: S[key];
} & { createdAt?: BaseSchema['createdAt'] };
export type TOptionalPropertyFrom<O extends object, O1 extends object> = Pick<
O1,
Exclude<keyof O1, keyof O>
> &
Pick<O, Exclude<keyof O, keyof O1>>;
export type OptionalProperties<T, K extends keyof T> = Omit<
T,
K | keyof BaseSchema
> &
Partial<Pick<T, K>>;
export type FixturesTypeBuilder<
S extends object,
D extends object,
DO = TFixturesDefaultValues<D>,
U = Partial<TFixtures<TOptionalPropertyFrom<D, S>>>,
> = {
defaultValues: DO & U;
values: OptionalProperties<S, keyof S & keyof (DO & U)>;
};

View File

@@ -0,0 +1,24 @@
/*
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
export enum DtoAction {
Create = 'create',
Read = 'read',
Update = 'update',
Delete = 'delete',
}
export type DtoConfig<
C extends Partial<Record<DtoAction, object>> = Partial<
Record<DtoAction, object>
>,
> = C;
export type DtoInfer<K extends keyof Dto, Dto, I> = Dto[K] extends object
? Dto[K]
: I;