ivanovna.orm/src/app/repo.ts

130 lines
3.8 KiB
TypeScript

import { Entity } from './entity'
import { Storage } from './storage'
/** @public */
export class ErrFoundNotUniqEntity extends Error {}
/** @public */
export class ErrEntityNotFound extends Error {}
/** @public */
export abstract class Repo<T extends Entity<any>> {
protected _storage: Storage
protected _entity: T
protected _limit = 0
protected _offset = 0
protected _transformer: (obj: any) => any
/**
* Возвращает объект соотв. сущности, например new App()
*/
abstract Entity(): T
/**
* Возвращает название коллекции/таблицы/... хранящей записи соотв. сущностей
*/
abstract Name(): string
constructor(storage: Storage, transformer?: (obj: any) => any) {
this._storage = storage
this._entity = this.Entity()
this.setTransformer(transformer)
}
setTransformer(transformer?: (obj: any) => any): this {
if (transformer) {
this._transformer = transformer
} else {
this._transformer = function (object: any): any {
const entity = this.Entity()
entity.data.fromObject(object)
entity._id = object._id
return entity
}
}
return this
}
get storage(): Storage {
return this._storage
}
async save(entity: T): Promise<this> {
const uniqKey = entity.data.uniqKey()
entity._id = await this._storage.save(this.Name(), uniqKey, entity.data.toObject())
return this
}
async findById(id: string): Promise<T | null> {
const uniqKey = this.Entity().data.uniqKey()
const cursor = await this._storage.find(this.Name(), { [uniqKey]: id })
const entryList = await cursor.toArray()
if (entryList.length > 1) {
throw new ErrFoundNotUniqEntity(
`found few (${entryList.length}) entries in ${this.Name()} for id ${id}`
)
}
if (entryList.length === 0) {
throw new ErrEntityNotFound(`not found entry in [${this.Name()}] for id [${id}]`)
}
return this._transformer(entryList[0])
}
/**
*
* @protected
*/
async _findByParams(
parameters: Record<string, any>,
limit?: number,
order?: Record<string, any>
): Promise<T[]> {
const cursor = await this._storage.find(this.Name(), parameters)
if (limit && limit > 0) await cursor.limit(limit)
if (order) await cursor.sort(order)
const data = await cursor.toArray()
const list = []
for (const item of data) {
list.push(this._transformer(item))
}
return list
}
async findMany(parameters: Record<string, any>, order?: Record<string, any>): Promise<T[]> {
const cursor = await this._storage.find(this.Name(), parameters)
if (this._offset > 0) await cursor.skip(this._offset)
if (this._limit > 0) await cursor.limit(this._limit)
if (order) await cursor.sort(order)
const data = await cursor.toArray()
const list = []
for (const item of data) {
list.push(this._transformer(item))
}
return list
}
shift(limit = 0, offset = 0) {
this._limit = limit
this._offset = offset
return this
}
async count(query?: Record<string, any>): Promise<number> {
return this._storage.count(this.Name(), query)
}
async remove(entity: T, silent = true): Promise<this> {
const idName = entity.data.uniqKey()
const id = entity.getUniqKey()
const ok = await this._storage.remove(this.Name(), idName, id)
if (!silent && !ok) throw new ErrEntityNotFound(`can not remove entry in ${this.Name()} for id ${id}`)
return this
}
}