import { Payload } from "../payload/Payload" ; import { DispatcherPayloadMeta , DispatcherPayloadMetaImpl , Transaction } from "../DispatcherPayloadMeta" ; import { Commitment , UnitOfWork } from "./UnitOfWork" ; import { MapLike } from "map-like" ; import { UseCaseExecutor } from "../UseCaseExecutor" ; import { Dispatcher } from "../Dispatcher" ; import { StoreGroupLike } from "../UILayer/StoreGroupLike" ; import { TransactionBeganPayload } from "../payload/TransactionBeganPayload" ; import { TransactionEndedPayload } from "../payload/TransactionEndedPayload" ; export interface UseCaseUnitOfWorkOptions { autoCommit : boolean ; } export interface UseCaseUnitOfWorkArgs { name : string ; dispatcher : Dispatcher ; storeGroup : StoreGroupLike ; options : UseCaseUnitOfWorkOptions ; } /**
 * UseCaseUnitOfWork is a Unit of Work between UseCase and StoreGroup.
 * It aim to manager updating of StoreGroup via UseCase.
 */ export class UseCaseUnitOfWork { name : string ; private unitOfWork : UnitOfWork ; private dispatcher : Dispatcher ; private storeGroup : StoreGroupLike ; private options : UseCaseUnitOfWorkOptions ; private unsubscribeMap : MapLike < UseCaseExecutor < any > , ( ) => void > ; private isTransactionWorking : boolean ; private doesReflectActionAtLeastOne : boolean ; constructor ( args ) { this . name = args . name ; this . storeGroup = args . storeGroup ; this . unitOfWork = new UnitOfWork ( this . storeGroup ) ; this . dispatcher = args . dispatcher ; this . options = args . options ; this . unsubscribeMap = new MapLike < UseCaseExecutor < any > , ( ) => void > ( ) ; this . isTransactionWorking = false ; this . doesReflectActionAtLeastOne = false ; if ( this . options . autoCommit ) { this . unitOfWork . onAddedCommitment ( ( ) => { this . unitOfWork . commit ( ) ; } ) ; } } /**
     * Unique id
     */ get id ( ) { return this . unitOfWork . id ; } /**
     * Is Unit of Work already disposed?
     */ get isDisposed ( ) { return this . unitOfWork . isDisposed ; } /**
     * current queued commitment size
     */ get size ( ) { return this . unitOfWork . size ; } /**
     * count of current working useCases
     */ get workingUseCaseCount ( ) { return this . unsubscribeMap . size ; } beginTransaction ( ) { const transaction = { id : this . id , name : this . name } ; this . isTransactionWorking = true ; const payload = new TransactionBeganPayload ( this . name ) ; const meta = new DispatcherPayloadMetaImpl ( { isTrusted : true , transaction : transaction } ) ; this . dispatcher . dispatch ( payload , meta ) ; this . unitOfWork . addCommitment ( { payload , meta , debugId : transaction . id } ) ; } /**
     * Begin transaction of the useCaseExecutor/UseCase
     */ open ( useCaseExecutor ) { const onDispatchOnUnitOfWork = ( payload , meta ) => { if ( this . isTransactionWorking ) { meta . transaction = { id : this . id , name : this . name } ; } this . unitOfWork . addCommitment ( { payload , meta , debugId : this . id } ) ; this . dispatcher . dispatch ( payload , meta ) ; } ; const unsubscribe = useCaseExecutor . onDispatch ( onDispatchOnUnitOfWork ) ; this . unsubscribeMap . set ( useCaseExecutor , unsubscribe ) ; } /**
     * Commit current queued payload to Committable StoreGroup.
     * After commit, prune current queue.
     *
     * ## Notes
     *
     * Do `commit()` and then this unit of work will be released.
     */ commit ( ) { if ( this . doesReflectActionAtLeastOne ) { throw new Error ( `Error(Transaction): This unit of work is already commit() or exit().
Not to allow to do multiple commits in a transaction` ) ; } if ( ! this . isTransactionWorking ) { this . unitOfWork . commit ( ) ; } else { const commitment = this . createTransactionEndPayload ( ) ; this . unitOfWork . addCommitment ( commitment ) ; this . unitOfWork . commit ( ) ; this . dispatcher . dispatch ( commitment . payload , commitment . meta ) ; this . isTransactionWorking = false ; } this . doesReflectActionAtLeastOne = true ; } private createTransactionEndPayload ( ) { const transaction = { id : this . id , name : this . name } ; const payload = new TransactionEndedPayload ( this . name ) ; const meta = new DispatcherPayloadMetaImpl ( { isTrusted : true , transaction : transaction } ) ; return { payload , meta , debugId : this . id } ; } /**
     * End transaction of the useCaseExecutor/UseCase
     */ close ( useCaseExecutor ) { const unsubscribe = this . unsubscribeMap . get ( useCaseExecutor ) ; if ( typeof unsubscribe !== "function" ) { console . error ( "Warning(UnitOfWork): This UseCaseExecutor is not opened or already closed." , useCaseExecutor ) ; return ; } unsubscribe ( ) ; this . unsubscribeMap . delete ( useCaseExecutor ) ; } exit ( ) { if ( this . doesReflectActionAtLeastOne ) { throw new Error ( `Error(Transaction): This unit of work is already commit() or exit().
Disallow to do multiple exit in a transaction` ) ; } this . doesReflectActionAtLeastOne = true ; if ( this . isTransactionWorking ) { const commitment = this . createTransactionEndPayload ( ) ; this . dispatcher . dispatch ( commitment . payload , commitment . meta ) ; this . isTransactionWorking = false ; } } /**
     * Release this Unit of Work.
     * After released, can't commit this Unit of Work.
     *
     * Please call `exit` or `commit at once before releasing.
     */ release ( ) { if ( ! this . options . autoCommit && ! this . doesReflectActionAtLeastOne ) { if ( process . env . NODE_ENV !== "production" ) { console . error ( `Warning(UnitOfWork): Transaction(${ this . name }) should be commit() or exit() at least one. 
If you not want to commit, Please call \`transactionContext.exit()\` at end of transaction.
` ) ; } this . exit ( ) ; } this . unsubscribeMap . keys ( ) . forEach ( ( useCaseExecutor ) => { this . close ( useCaseExecutor ) ; } ) ; this . unitOfWork . release ( ) ; } }