import { observable , makeObservable , action , toJS } from 'mobx' ; import type { IConnectionExecutionContext } from '@cloudbeaver/core-connections' ; import { ResultDataFormat } from '@cloudbeaver/core-sdk' ; import { DatabaseDataActions } from './DatabaseDataActions' ; import type { IDatabaseDataAction , IDatabaseDataActionClass , IDatabaseDataActionInterface } from './IDatabaseDataAction' ; import type { IDatabaseDataActions } from './IDatabaseDataActions' ; import type { IDatabaseDataResult } from './IDatabaseDataResult' ; import { DatabaseDataAccessMode , IDatabaseDataSource , IRequestInfo } from './IDatabaseDataSource' ; export abstract class DatabaseDataSource < TOptions , TResult extends IDatabaseDataResult > implements IDatabaseDataSource < TOptions , TResult > { access : DatabaseDataAccessMode ; dataFormat : ResultDataFormat ; supportedDataFormats : ResultDataFormat [ ] ; actions : IDatabaseDataActions < TOptions , TResult > ; results : TResult [ ] ; offset : number ; count : number ; prevOptions : Readonly < TOptions > | null ; options : TOptions | null ; requestInfo : IRequestInfo ; error : Error | null ; executionContext : IConnectionExecutionContext | null ; abstract get canCancel ( ) : boolean ; protected disabled : boolean ; private activeRequest : Promise < TResult [ ] | null > | null ; private activeSave : Promise < TResult [ ] > | null ; private activeTask : Promise < any > | null ; private lastAction : ( ) => Promise < void > ; constructor ( ) { this . actions = new DatabaseDataActions ( this ) ; this . access = DatabaseDataAccessMode . Default ; this . results = [ ] ; this . offset = 0 ; this . count = 0 ; this . prevOptions = null ; this . options = null ; this . disabled = false ; this . activeRequest = null ; this . activeSave = null ; this . activeTask = null ; this . executionContext = null ; this . dataFormat = ResultDataFormat . Resultset ; this . supportedDataFormats = [ ] ; this . requestInfo = { requestDuration : 0 , requestMessage : '' , requestFilter : '' , source : null , } ; this . error = null ; this . lastAction = this . requestData . bind ( this ) ; makeObservable < DatabaseDataSource < TOptions , TResult > , 'activeRequest' | 'activeSave' | 'activeTask' | 'disabled' > ( this , { access : observable , dataFormat : observable , supportedDataFormats : observable , results : observable , offset : observable , count : observable , prevOptions : observable , options : observable , requestInfo : observable , error : observable . ref , executionContext : observable , disabled : observable , activeRequest : observable . ref , activeSave : observable . ref , activeTask : observable . ref , setResults : action , setSupportedDataFormats : action , } ) ; } tryGetAction < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) ; tryGetAction < T extends IDatabaseDataAction < TOptions , TResult > > ( result , action ) ; tryGetAction < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) { if ( typeof resultIndex === 'number' ) { if ( ! this . hasResult ( resultIndex ) ) { return undefined ; } return this . actions . tryGet ( this . results [ resultIndex ] , action ) ; } return this . actions . tryGet ( resultIndex , action ) ; } getAction < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) ; getAction < T extends IDatabaseDataAction < TOptions , TResult > > ( result , action ) ; getAction < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) { if ( typeof resultIndex === 'number' ) { if ( ! this . hasResult ( resultIndex ) ) { throw new Error ( 'Result index out of range' ) ; } return this . actions . get ( this . results [ resultIndex ] , action ) ; } return this . actions . get ( resultIndex , action ) ; } getActionImplementation < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) ; getActionImplementation < T extends IDatabaseDataAction < TOptions , TResult > > ( result , action ) ; getActionImplementation < T extends IDatabaseDataAction < TOptions , TResult > > ( resultIndex , action ) { if ( typeof resultIndex === 'number' ) { if ( ! this . hasResult ( resultIndex ) ) { return undefined ; } return this . actions . getImplementation ( this . results [ resultIndex ] , action ) ; } return this . actions . getImplementation ( resultIndex , action ) ; } abstract cancel ( ) ; hasResult ( resultIndex ) { return resultIndex < this . results . length ; } getResult ( index ) { if ( this . results . length > index ) { return this . results [ index ] ; } return null ; } setResults ( results ) { this . results = results ; this . actions . updateResults ( results ) ; return this ; } isReadonly ( ) { return this . access === DatabaseDataAccessMode . Readonly || this . results . length > 1 || ! this . executionContext ?. context || this . disabled ; } isLoading ( ) { return ! ! this . activeRequest || ! ! this . activeSave || ! ! this . activeTask ; } isDisabled ( resultIndex ) { return this . isLoading ( ) || this . disabled ; } setAccess ( access ) { this . access = access ; return this ; } setSlice ( offset , count ) { this . offset = offset ; this . count = count ; return this ; } setOptions ( options ) { this . options = options ; return this ; } setDataFormat ( dataFormat ) { this . dataFormat = dataFormat ; return this ; } setSupportedDataFormats ( dataFormats ) { this . supportedDataFormats = dataFormats ; if ( ! this . supportedDataFormats . includes ( this . dataFormat ) ) { this . dataFormat = dataFormats [ 0 ] ; } return this ; } setExecutionContext ( context ) { this . executionContext = context ; return this ; } async retry ( ) { await this . lastAction ( ) ; } async runTask < T > ( task ) { if ( this . activeTask ) { try { await this . activeTask ; } catch { } } if ( this . activeSave ) { try { await this . activeSave ; } catch { } } if ( this . activeRequest ) { try { await this . activeRequest ; } catch { } } this . activeTask = task ( ) ; try { return await this . activeTask ; } finally { this . activeTask = null ; } } async requestData ( ) { if ( this . activeSave ) { try { await this . activeSave ; } finally { } } if ( this . activeRequest ) { await this . activeRequest ; return ; } this . lastAction = this . requestData . bind ( this ) ; try { this . activeRequest = this . requestDataAction ( ) ; const data = await this . activeRequest ; if ( data !== null ) { this . setResults ( data ) ; } } finally { this . activeRequest = null ; } } async refreshData ( ) { if ( this . prevOptions ) { this . options = toJS ( this . prevOptions ) ; } await this . requestData ( ) ; } async saveData ( ) { if ( this . activeRequest ) { try { await this . activeRequest ; } finally { } } if ( this . activeSave ) { await this . activeSave ; return ; } this . lastAction = this . saveData . bind ( this ) ; try { const promise = this . save ( this . results ) ; if ( promise instanceof Promise ) { this . activeSave = promise ; } this . setResults ( await promise ) ; } finally { this . activeSave = null ; } } clearError ( ) { this . error = null ; } resetData ( ) { if ( this . activeSave || this . activeRequest ) { return ; } this . setResults ( [ ] ) ; } abstract request ( prevResults ) ; abstract save ( prevResults ) ; abstract dispose ( ) ; async requestDataAction ( ) { this . prevOptions = toJS ( this . options ) ; return this . request ( this . results ) ; } }