import { decorate , injectable } from 'inversify' ; import { Schema , Model , Document } from 'mongoose' ; import * as uniqueValidator from 'mongoose-unique-validator' ; import { IBaseRepository } from '../IBaseRepository' ; import { ApiError } from '../../config/ErrorHandler' ; import constants from '../../config/constants' ; import { MongoDbConnection } from '../../config/MongoDbConnection' ; import { cleanQuery } from '../../utils' ; export abstract class BaseRepository < EntityType > implements IBaseRepository < EntityType > { protected dbConnection : MongoDbConnection ; protected schema : Schema ; protected documentModel : Model < Document > ; protected modelName : string ; protected formatter : any = Object ; private initiated : boolean = false ; /** this needs to be called after the extended class super is executed */ protected init ( ) { if ( this . initiated ) return ; this . documentModel = this . dbConnection . db . model ( this . modelName , this . schema ) ; this . schema . plugin ( uniqueValidator ) ; this . initiated = true ; } public async create ( model ) { const document = await this . documentModel . create ( this . cleanToSave ( model ) ) ; return new this . formatter ( document ) ; } public async update ( _id , model ) { await this . documentModel . updateOne ( { _id } , this . cleanToSave ( model ) ) ; } public async delete ( _id ) { return this . documentModel . deleteOne ( { _id } ) ; } public async find ( skip = 0 , limit = 250 , sort , query ) { const sortObject = cleanQuery ( sort , this . sortQueryFormatter ) ; return ( await this . documentModel . find ( this . cleanWhereQuery ( query ) ) . sort ( Object . keys ( sortObject ) . map ( key => [ key , sortObject [ key ] ] ) ) . skip ( skip ) . limit ( limit ) ) . map ( item => new this . formatter ( item ) ) ; } public async findOne < T > ( query ) { const document = await this . documentModel . findOne ( query ) ; if ( ! document ) throw new ApiError ( constants . errorTypes . notFound ) ; return new this . formatter ( document ) ; } public async count ( query ) { return this . documentModel . count ( this . cleanWhereQuery ( query ) ) ; } protected cleanToSave ( entity ) { const copy = new this . formatter ( entity ) ; const loop = ( value ) : any => { if ( ! value || typeof value !== 'object' ) return ; /** formatting logic to save goes here */ Object . keys ( value ) . forEach ( key => loop ( value [ key ] ) ) ; } ; loop ( copy ) ; return copy ; } protected sortQueryFormatter ( key , value ) { if ( value === 'asc' ) return 1 ; if ( value === 'desc' ) return - 1 ; return undefined ; } protected cleanWhereQuery ( query ) { if ( ! query || typeof query === 'string' ) return cleanQuery ( query ) ; const newQuery = { $or : [ ] } ; Object . keys ( query ) . forEach ( key => { let value = query [ key ] ; if ( ! ( value instanceof Array ) ) newQuery [ key ] = value ; else newQuery . $or = newQuery . $or . concat ( value . map ( item => ( { [ key ] : item } ) ) ) ; } ) ; if ( ! newQuery . $or . length ) delete newQuery . $or ; return newQuery ; } } decorate ( injectable ( ) , BaseRepository ) ;