generator-kube-microservice-node
Version:
A k8s micro-service generator with deployment, service, Dockerfile. Built with express/mongo/redis
381 lines (364 loc) • 15.6 kB
text/typescript
import { Document, Model, Schema, ClientSession } from 'mongoose';
import { IMongoModel, MongoTypes, Pagination, Either, DeleteOp } from '../interfaces/SharedInterfaces';
import { injectable, inject, unmanaged } from 'inversify';
import Connection from './Connection';
import EntityNotFoundException from '../exceptions/EntityNotFoundException';
import env from '../../config/env';
import GenericException from '../exceptions/GenericException';
import REFERENCES from '../../config/inversify.references';
type InterfaceBoolean<T> = { [P in keyof T]?: boolean };
type InterfacePagination<T> = { [P in keyof T]?: 1 | -1 };
type InterfacePropertiesToString<T> = keyof T;
interface ICommonParamsSession<BooleanType> {
databaseName?: string;
session?: ClientSession;
throwErrors?: BooleanType;
}
interface ICommonParamsFilter<T, BooleanType> {
filter?: MongoTypes<T>;
databaseName?: string;
throwErrors?: BooleanType;
}
interface IFindParams<ReturnType, BooleanType extends boolean> extends ICommonParamsFilter<ReturnType, BooleanType> {
pagination?: Pagination;
sort?: InterfacePagination<ReturnType>;
fieldsToShow?: InterfaceBoolean<ReturnType>;
}
interface IDistinctParams<T, BooleanType> extends ICommonParamsFilter<T, BooleanType> {
field: InterfacePropertiesToString<T>;
}
interface ICountParams<T, BooleanType extends boolean> extends ICommonParamsFilter<T, BooleanType> {}
interface IInsertParams<ReturnType, BooleanType extends boolean> extends ICommonParamsSession<BooleanType> {
entity: ReturnType;
}
interface IInsertManyParams<ReturnType, BooleanType extends boolean> extends ICommonParamsSession<BooleanType> {
entities: ReturnType[];
}
interface IFindByIdParams<ReturnType, BooleanType extends boolean> {
id: string;
fieldsToShow?: InterfaceBoolean<ReturnType>;
throwErrors?: BooleanType;
databaseName?: string;
}
interface IFindOneParams<ReturnType, BooleanType extends boolean> extends ICommonParamsFilter<ReturnType, BooleanType> {
fieldsToShow?: InterfaceBoolean<ReturnType>;
}
interface IDeleteParams<BooleanType extends boolean> extends ICommonParamsSession<BooleanType> {
id: string;
}
interface IDeleteManyParams<T, BooleanType extends boolean> extends ICommonParamsSession<BooleanType> {
filter?: MongoTypes<T>;
}
interface IUpdateParams<ReturnType, BooleanType extends boolean> extends ICommonParamsSession<BooleanType> {
entity: Partial<ReturnType>;
conditions: MongoTypes<ReturnType>;
upsert?: boolean;
}
@injectable()
export class MongoService<Interface extends IMongoModel> {
@inject(REFERENCES.Connection) private _connection: Connection;
private _model: Model<Document>;
private _modelSchema: Schema<Interface>;
private _defaultDB: string;
constructor(@unmanaged() model: Model<Document>, @unmanaged() modelSchema: Schema<Interface>) {
this._model = model;
this._modelSchema = modelSchema;
this._defaultDB = env.mongodb_database_name;
}
/**
* Saves the new Mongoose Model
* @param params.entity A object that matchs a mongoose schema
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.session A mongoose session to handle transactions
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
insert(params: IInsertParams<Interface, false>): Promise<Either<Interface>>;
insert(params: IInsertParams<Interface, true>): Promise<Interface>;
insert(params: IInsertParams<Interface, boolean>): Promise<Either<Interface> | Interface> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const model: Document = new _model(params.entity);
const result: Interface = (await model.save({ session: params.session })) as any;
if (params.throwErrors) {
return resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Finds a Document by ObjectId
* @param params.id A ObjectId from Mongoose schema
* @param params.fieldsToShow Object containing the fields to return from the Documents
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
* @returns A Promise with a single Document
*/
findById(params: IFindByIdParams<Interface, false>): Promise<Either<Interface>>;
findById(params: IFindByIdParams<Interface, true>): Promise<Interface>;
findById(params: IFindByIdParams<Interface, boolean>): Promise<Either<Interface> | Interface> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = (await _model.findById({ _id: params.id }, params.fieldsToShow).lean(true)) as any;
if (!result) {
return resolve([new EntityNotFoundException({ _id: params.id }), null]);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Finds multiple Documents
* @param params.filter Object used to filter Documents
* @param params.pagination Object with skip, limit properties to control pagination
* @param params.fieldsToShow Object containing the fields to return from the Documents
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
* @returns A Promise with a Array of Documents found
*/
find(params: IFindParams<Interface, false>): Promise<Either<Interface[]>>;
find(params: IFindParams<Interface, true>): Promise<Interface[]>;
find(params: IFindParams<Interface, boolean>): Promise<Either<Interface[]> | Interface[]> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result: Interface[] = await _model
.find(params.filter, params.fieldsToShow, params.pagination)
.sort(params.sort)
.lean(true);
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Finds the first Document that matchs the params
* @param params.filter Object used to filter Documents
* @param params.fieldsToShow Object containing the fields to return from the Documents
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
* @returns A Promise with a single Document
*/
findOne(params: IFindOneParams<Interface, false>): Promise<Either<Interface>>;
findOne(params: IFindOneParams<Interface, true>): Promise<Interface>;
findOne(params: IFindOneParams<Interface, boolean>): Promise<Either<Interface> | Interface> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = (await _model.findOne(params.filter, params.fieldsToShow).lean(true)) as any;
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Deletes a Mongoose Document
* @param params.id A ObjectId from Mongoose schema
* @param parms.databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
delete(params: IDeleteParams<false>): Promise<Either<DeleteOp>>;
delete(params: IDeleteParams<true>): Promise<DeleteOp>;
delete(params: IDeleteParams<boolean>): Promise<Either<DeleteOp> | DeleteOp> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = (await _model.deleteOne({ _id: params.id }, { session: params.session }).lean(true)) as any;
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Delete many documents
* @param params.fieldsToShow Object containing the fields to return from the Documents
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
deleteMany(params: IDeleteManyParams<Interface, false>): Promise<Either<DeleteOp>>;
deleteMany(params: IDeleteManyParams<Interface, true>): Promise<DeleteOp>;
deleteMany(params: IDeleteManyParams<Interface, boolean>): Promise<Either<DeleteOp> | DeleteOp> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
// The @ts-ignore above is here because @types/mongoose don't have correct typings for ModelOptions
// @ts-ignore
const result = await _model.deleteMany(params.filter, { session: params.session }).exec();
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Updates a Document
* @param params.entity A object that matchs a mongoose schema with a currently know ObjectId
* @param params.databaseName Set this to query on another database in the current mongo connection
* @param params.session A mongoose session to handle transactions
* @param params.throwErrors Enable classical try/catch way of handling errors
* @param params.upsert If true, and no documents found, insert a new document
*/
update(params: IUpdateParams<Interface, false>): Promise<Either<Interface>>;
update(params: IUpdateParams<Interface, true>): Promise<Interface>;
update(params: IUpdateParams<Interface, boolean>): Promise<Either<Interface> | Interface> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result: Interface = (await _model
.findOneAndUpdate(params.conditions, params.entity, {
session: params.session,
upsert: params.upsert,
new: params.upsert,
})
.exec()) as any;
if (!result) {
throw new EntityNotFoundException(params.conditions);
}
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Save multiple documents
* @param entities Array os objects to save
* @param databaseName Set this to query on another database in the current mongo connection
* @param session A mongoose session to handle transactions
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
insertMany(params: IInsertManyParams<Interface, false>): Promise<Either<Interface[]>>;
insertMany(params: IInsertManyParams<Interface, true>): Promise<Interface[]>;
insertMany(params: IInsertManyParams<Interface, boolean>): Promise<Either<Interface[]> | Interface[]> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = (await _model.insertMany(params.entities, { session: params.session })) as any;
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Get the count of documents by a given filter
* @param filter Object used to filter Documents
* @param databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
count(params: ICountParams<Interface, false>): Promise<Either<number>>;
count(params: ICountParams<Interface, true>): Promise<number>;
count(params: ICountParams<Interface, boolean>): Promise<Either<number> | number> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = await _model.count(params.filter).exec();
if (params.throwErrors) {
resolve(result);
}
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Return a distinct operation
* @param field A string representing a field from [[Interface]]
* @param filter Object used to filter Documents
* @param databaseName Set this to query on another database in the current mongo connection
* @param params.throwErrors Enable classical try/catch way of handling errors
*/
distinct<T>(params: IDistinctParams<Interface, false>): Promise<Either<T[]>>;
distinct<T>(params: IDistinctParams<Interface, true>): Promise<T[]>;
distinct<T>(params: IDistinctParams<Interface, boolean>): Promise<Either<T[]> | T[]> {
return new Promise(async (resolve, reject) => {
try {
const _model = this.getModel(params.databaseName);
const result = await _model.distinct(params.field as string, params.filter).exec();
resolve([null, result]);
} catch (e) {
const exception = new GenericException({ name: e.name, message: e.message });
if (params.throwErrors) {
reject(exception);
}
resolve([exception, null]);
}
});
}
/**
* Gets a model instance from a given database on the current connection
* @param databaseName database name
*/
getModel(databaseName: string = this._defaultDB): Model<Document> {
let _model = this._model;
if (databaseName) {
const conn = this._connection.useDB(databaseName);
_model = conn.model(this._model.modelName, this._modelSchema);
}
return _model;
}
}