UNPKG

@furystack/repository

Version:

Repository implementation for FuryStack

174 lines (163 loc) 6.43 kB
import type { CreateResult, FilterType, FindOptions, PartialResult, WithOptionalId } from '@furystack/core' import { AuthorizationError } from '@furystack/core' import type { Injector } from '@furystack/inject' import { EventHub } from '@furystack/utils' import type { DataSetSettings } from './data-set-setting.js' /** * An authorized Repository Store instance */ export class DataSet<T, TPrimaryKey extends keyof T, TWritableData = WithOptionalId<T, TPrimaryKey>> extends EventHub<{ onEntityAdded: { injector: Injector; entity: T } onEntityUpdated: { injector: Injector; id: T[TPrimaryKey]; change: Partial<T> } onEntityRemoved: { injector: Injector; key: T[TPrimaryKey] } }> implements Disposable { /** * Primary key of the contained entity */ public primaryKey: TPrimaryKey /** * Adds an entity to the DataSet * @param injector The injector from the context * @param entities The entities to add * @returns The CreateResult with the created entities */ public async add(injector: Injector, ...entities: TWritableData[]): Promise<CreateResult<T>> { await Promise.all( entities.map(async (entity) => { if (this.settings.authorizeAdd) { const result = await this.settings.authorizeAdd({ injector, entity }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } }), ) const parsed = await Promise.all( entities.map(async (entity) => { return this.settings.modifyOnAdd ? await this.settings.modifyOnAdd({ injector, entity }) : entity }), ) const createResult = await this.settings.physicalStore.add(...parsed) createResult.created.map((entity) => { this.emit('onEntityAdded', { injector, entity }) }) return createResult } /** * Updates an entity in the store * @param injector The injector from the context * @param id The identifier of the entity * @param change The update */ public async update(injector: Injector, id: T[TPrimaryKey], change: Partial<T>): Promise<void> { if (this.settings.authorizeUpdate) { const result = await this.settings.authorizeUpdate({ injector, change }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } if (this.settings.authorizeUpdateEntity) { const entity = await this.settings.physicalStore.get(id) if (entity) { const result = await this.settings.authorizeUpdateEntity({ injector, change, entity }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } } const parsed = this.settings.modifyOnUpdate ? await this.settings.modifyOnUpdate({ injector, id, entity: change }) : change await this.settings.physicalStore.update(id, parsed) this.emit('onEntityUpdated', { injector, change: parsed, id }) } /** * Returns a Promise with the entity count * @param injector The Injector from the context * @param filter The Filter that will be applied * @returns the Count */ public async count(injector: Injector, filter?: FilterType<T>): Promise<number> { if (this.settings.authorizeGet) { const result = await this.settings.authorizeGet({ injector }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } return await this.settings.physicalStore.count(filter) } /** * Returns a filtered subset of the entity * @param injector The Injector from the context * @param filter The Filter definition * @returns A result with the current items */ public async find<TFields extends Array<keyof T>>( injector: Injector, filter: FindOptions<T, TFields>, ): Promise<Array<PartialResult<T, TFields>>> { if (this.settings.authorizeGet) { const result = await this.settings.authorizeGet({ injector }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } const parsedFilter = this.settings.addFilter ? await this.settings.addFilter({ injector, filter }) : filter return this.settings.physicalStore.find(parsedFilter) } /** * Returns an entity based on its primary key * @param injector The injector from the context * @param key The identifier of the entity * @param select A field list used for projection * @returns An item with the current unique key or Undefined */ public async get(injector: Injector, key: T[TPrimaryKey], select?: Array<keyof T>) { if (this.settings.authorizeGet) { const result = await this.settings.authorizeGet({ injector }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } const instance = await this.settings.physicalStore.get(key, select) if (instance && this.settings && this.settings.authorizeGetEntity) { const result = await this.settings.authorizeGetEntity({ injector, entity: instance }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } return instance } /** * Removes an entity based on its primary key * @param injector The Injector from the context * @param key The primary key * @returns A promise that will be resolved / rejected based on the remove success */ public async remove(injector: Injector, key: T[TPrimaryKey]): Promise<void> { if (this.settings.authorizeRemove) { const result = await this.settings.authorizeRemove({ injector }) if (!result.isAllowed) { throw new AuthorizationError(result.message) } } if (this.settings.authorizeRemoveEntity) { const entity = await this.settings.physicalStore.get(key) if (entity) { const removeResult = await this.settings.authorizeRemoveEntity({ injector, entity }) if (!removeResult.isAllowed) { throw new AuthorizationError(removeResult.message) } } } await this.settings.physicalStore.remove(key) this.emit('onEntityRemoved', { injector, key }) } constructor(public readonly settings: DataSetSettings<T, TPrimaryKey, TWritableData>) { super() this.primaryKey = this.settings.physicalStore.primaryKey } }