hexabot/api/src/utils/generics/base-service.ts
2025-01-16 18:47:25 +01:00

198 lines
5.3 KiB
TypeScript

/*
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { ConflictException } from '@nestjs/common';
import { ClassTransformOptions } from 'class-transformer';
import { MongoError } from 'mongodb';
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';
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, Dto>,
) {}
getRepository() {
return this.repository;
}
async findOne(
criteria: string | TFilterQuery<T>,
options?: ClassTransformOptions,
projection?: ProjectionType<T>,
): Promise<T | null> {
return await this.repository.findOne(criteria, options, projection);
}
async findOneAndPopulate(
criteria: string | TFilterQuery<T>,
projection?: ProjectionType<T>,
) {
return await this.repository.findOneAndPopulate(criteria, projection);
}
async find(
filter: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>,
): Promise<T[]>;
/**
* @deprecated
*/
async find(
filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>,
): Promise<T[]>;
async find(
filter: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>,
): Promise<T[]> {
if (Array.isArray(pageQuery))
return await this.repository.find(filter, pageQuery, projection);
return await this.repository.find(filter, pageQuery, projection);
}
async findAndPopulate(
filters: TFilterQuery<T>,
pageQuery?: PageQueryDto<T>,
projection?: ProjectionType<T>,
): Promise<TFull[]>;
/**
* @deprecated
*/
async findAndPopulate(
filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T>,
projection?: ProjectionType<T>,
): Promise<TFull[]>;
async findAndPopulate(
filters: TFilterQuery<T>,
pageQuery?: QuerySortDto<T> | PageQueryDto<T>,
projection?: ProjectionType<T>,
): Promise<TFull[]> {
if (Array.isArray(pageQuery))
return await this.repository.findAndPopulate(
filters,
pageQuery,
projection,
);
return await this.repository.findAndPopulate(
filters,
pageQuery,
projection,
);
}
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);
}
/**
* @deprecated
*/
async findPage(
filters: TFilterQuery<T>,
pageQueryDto: PageQueryDto<T>,
): Promise<T[]> {
return await this.repository.findPage(filters, pageQueryDto);
}
/**
* @deprecated
*/
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();
}
async count(criteria?: TFilterQuery<T>): Promise<number> {
return await this.repository.count(criteria);
}
async create(dto: DtoInfer<DtoAction.Create, Dto, U>): Promise<T> {
try {
return await this.repository.create(dto);
} catch (error) {
if (error instanceof MongoError && error.code === 11000) {
throw new ConflictException(
'Duplicate key error: element already exists',
);
}
throw error;
}
}
async findOneOrCreate(
criteria: string | TFilterQuery<T>,
dto: DtoInfer<DtoAction.Create, Dto, U>,
): Promise<T> {
const result = await this.findOne(criteria);
if (!result) {
return await this.create(dto);
}
return result;
}
async createMany(
dtoArray: DtoInfer<DtoAction.Create, Dto, U>[],
): Promise<T[]> {
return await this.repository.createMany(dtoArray);
}
async updateOne(
criteria: string | TFilterQuery<T>,
dto: DtoInfer<DtoAction.Update, Dto, Partial<U>>,
options?: QueryOptions<Partial<U>> | null,
): Promise<T> {
return await this.repository.updateOne(criteria, dto, options);
}
async updateMany(filter: TFilterQuery<T>, dto: Partial<U>) {
return await this.repository.updateMany(filter, dto);
}
async deleteOne(criteria: string | TFilterQuery<T>) {
return await this.repository.deleteOne(criteria);
}
async deleteMany(filter: TFilterQuery<T>) {
return await this.repository.deleteMany(filter);
}
}