import { merge, pick } from 'lodash'; import { Id, Shape, HasId } from '../types'; import { BaseModel } from './base'; import { Keys, Entities } from './entities'; /** * @param {BaseModel} parent The parent BaseModel * @param {string} relationKey The name of the property on the parent to refer the relation to (e.g. users.forms where forms is the relationKey) * @param {string} foreignKey The foreignKey on the relation refering to the parent. (e.g userId on a form) * @param {Keys} storeKey The store key of the relation items. */ export class HasMany { protected _parent: BaseModel; public _foreignKey: string; public _relationKey: string; public _storeKey: Keys; constructor( parent: BaseModel, relationKey: string, foreignKey: string, storeKey: Keys, ) { this._parent = parent; this._foreignKey = foreignKey; this._relationKey = relationKey; this._storeKey = storeKey; } async create(parentId: Id, item: DataType): Promise { const actualId = await this._parent.getId(parentId); const data = await this._parent._requestModel[this._relationKey].post( actualId, item, ); this._parent._store.set(this._storeKey, data, { deep: false }); return data; } async getLocalAll(parentId: Id): Promise { const actualId = await this._parent.getId(parentId); return ( this._parent._store.filter( this._storeKey, (item) => item[this._foreignKey] === actualId, ) ); } async getLocalById(id: Id) { const actualId = await this._parent.getId(id); return this._parent._store.get(this._storeKey, actualId); } async getAll(parentId: Id, filter?: Shape): Promise { const actualId = await this._parent.getId(parentId); const data = await this._parent._requestModel[this._relationKey].getAll( actualId, ); this._parent._store.setBySchema([Entities[this._storeKey]], data, { deep: false, }); return data; } // TODO Respects the where-clause in the cached items. async updateAll( parentId: Id, item: Partial, where?: Shape, ): Promise { const actualId = await this._parent.getId(parentId); await this._parent._requestModel[this._relationKey].patchAll( actualId, item, where, ); const olds = this._parent._store.filter( this._storeKey, (old) => old[this._foreignKey] === actualId, ); const updates = ( olds.map((old) => merge({}, old, item, pick(old, 'id'))) ); this._parent._store.setBySchema([Entities[this._storeKey]], updates); return updates; } // TODO Respects the where-clause in the cached items. async delAll(parentId: Id, where?: Shape) { const actualId = await this._parent.getId(parentId); await this._parent._requestModel[this._relationKey].delAll(actualId, where); const ids = this._parent._store .filter(this._storeKey, (item) => item[this._foreignKey] === actualId) .map((i: HasId) => i.id); this._parent._store.del(this._storeKey, ids); } } export function hasMany({ relationKey, foreignKey, storeKey, }: { relationKey: string; foreignKey: string; storeKey: Keys; }) { return function _DecoratorName( ctor: C, ) { return class extends ctor { constructor(...args: any[]) { super(...args); this[relationKey] = new HasMany( >(this), relationKey, foreignKey, storeKey, ); } }; }; }