import { Class, Client, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, ModelDb, Ref, Tx, TxResult, WithLookup, toFindResult, SearchQuery, SearchOptions, SearchResult } from '@hcengineering/core' import { Resource } from '@hcengineering/platform' /** * @public */ export interface PresentationMiddleware { next?: PresentationMiddleware tx: (tx: Tx) => Promise notifyTx: (tx: Tx) => Promise findAll: ( _class: Ref>, query: DocumentQuery, options?: FindOptions ) => Promise> findOne: ( _class: Ref>, query: DocumentQuery, options?: FindOptions ) => Promise | undefined> subscribe: ( _class: Ref>, query: DocumentQuery, options: FindOptions | undefined, refresh: () => void ) => Promise<{ unsubscribe: () => void query?: DocumentQuery options?: FindOptions }> close: () => Promise } /** * @public */ export type PresentationMiddlewareCreator = (client: Client, next?: PresentationMiddleware) => PresentationMiddleware /** * @public */ export interface PresentationPipeline extends Client, Exclude { close: () => Promise } /** * @public */ export class PresentationPipelineImpl implements PresentationPipeline { private head: PresentationMiddleware | undefined private constructor (readonly client: Client) {} getHierarchy (): Hierarchy { return this.client.getHierarchy() } getModel (): ModelDb { return this.client.getModel() } async notifyTx (tx: Tx): Promise { await this.head?.notifyTx(tx) } static create (client: Client, constructors: PresentationMiddlewareCreator[]): PresentationPipeline { const pipeline = new PresentationPipelineImpl(client) pipeline.head = pipeline.buildChain(constructors) return pipeline } private buildChain (constructors: PresentationMiddlewareCreator[]): PresentationMiddleware | undefined { let current: PresentationMiddleware | undefined for (let index = constructors.length - 1; index >= 0; index--) { const element = constructors[index] current = element(this.client, current) } return current } async findAll( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise> { return this.head !== undefined ? await this.head.findAll(_class, query, options) : await this.client.findAll(_class, query, options) } async searchFulltext (query: SearchQuery, options: SearchOptions): Promise { return await this.client.searchFulltext(query, options) } async findOne( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise | undefined> { return this.head !== undefined ? await this.head.findOne(_class, query, options) : await this.client.findOne(_class, query, options) } async subscribe( _class: Ref>, query: DocumentQuery, options: FindOptions | undefined, refresh: () => void ): Promise<{ unsubscribe: () => void query?: DocumentQuery options?: FindOptions }> { return this.head !== undefined ? await this.head.subscribe(_class, query, options, refresh) : { unsubscribe: () => {} } } async tx (tx: Tx): Promise { if (this.head === undefined) { return await this.client.tx(tx) } else { return await this.head.tx(tx) } } async close (): Promise { await this.head?.close() } } /** * @public */ export abstract class BasePresentationMiddleware { constructor (protected readonly client: Client, readonly next?: PresentationMiddleware) {} async provideNotifyTx (tx: Tx): Promise { await this.next?.notifyTx(tx) } async provideClose (): Promise { await this.next?.close() } async findAll( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise> { return await this.provideFindAll(_class, query, options) } async findOne( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise | undefined> { return await this.provideFindOne(_class, query, options) } async subscribe( _class: Ref>, query: DocumentQuery, options: FindOptions | undefined, refresh: () => void ): Promise<{ unsubscribe: () => void query?: DocumentQuery options?: FindOptions }> { return await this.provideSubscribe(_class, query, options, refresh) } protected async provideTx (tx: Tx): Promise { if (this.next !== undefined) { return await this.next.tx(tx) } return await this.client.tx(tx) } protected async provideFindAll( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise> { if (this.next !== undefined) { return await this.next.findAll(_class, query, options) } return await this.client.findAll(_class, query, options) } protected async provideFindOne( _class: Ref>, query: DocumentQuery, options?: FindOptions ): Promise | undefined> { if (this.next !== undefined) { return await this.next.findOne(_class, query, options) } return await this.client.findOne(_class, query, options) } protected async provideSubscribe( _class: Ref>, query: DocumentQuery, options: FindOptions | undefined, refresh: () => void ): Promise<{ unsubscribe: () => void query?: DocumentQuery options?: FindOptions }> { if (this.next !== undefined) { return await this.next.subscribe(_class, query, options, refresh) } return { unsubscribe: () => {} } } } /** * @public */ export interface PresentationMiddlewareFactory extends Doc { createPresentationMiddleware: Resource } /** * @public */ export class OptimizeQueryMiddleware extends BasePresentationMiddleware implements PresentationMiddleware { private constructor (client: Client, next?: PresentationMiddleware) { super(client, next) } static create (client: Client, next?: PresentationMiddleware): OptimizeQueryMiddleware { return new OptimizeQueryMiddleware(client, next) } async notifyTx (tx: Tx): Promise { await this.provideNotifyTx(tx) } async close (): Promise { return await this.provideClose() } async tx (tx: Tx): Promise { return await this.provideTx(tx) } async subscribe( _class: Ref>, query: DocumentQuery, options: FindOptions | undefined, refresh: () => void ): Promise<{ unsubscribe: () => void query?: DocumentQuery options?: FindOptions }> { return await this.provideSubscribe(_class, query, options, refresh) } async findAll( _class: Ref>, query: DocumentQuery, options?: FindOptions | undefined ): Promise> { if (_class == null || typeof query !== 'object' || ('_class' in query && query._class == null)) { console.error('_class must be specified in query', query) return toFindResult([], 0) } return await this.provideFindAll(_class, query, options) } }