import { LogAndError } from '@birbal/common-utilities/dist/error'; import { VirtualStateModel } from '../../Model/Virtual/Meta/state'; import { VirtualStateHistoryModel } from '../../Model/Virtual/Meta/state-history'; import { VirtualRegistryModel, IVirtualRegistryBody } from '../../Model/Virtual/Meta/registry'; import * as UUID from 'uuid'; import { VirtualStatusCheck } from './status-check'; export enum EVirtualState { PENDING = 'pending' , SUBMITTED = "submitted" , REMOVED = "removed" , REJECTED = "rejected" , APPROVED = 'approved' , REVOKED = 'revoked' , EXPIRED = 'expired' } export class VirtualStateMachine { private DATABASE_NAME?: string; constructor(dbName: string) { this.DATABASE_NAME = dbName; } async syncAllTables(){ try{ if (!this.DATABASE_NAME) { throw new LogAndError("Invalid db name"); } let registryModel = new VirtualRegistryModel(this.DATABASE_NAME); await registryModel.defineSchema(); await registryModel.syncSchema(); let stateModel = new VirtualStateModel(this.DATABASE_NAME); await stateModel.defineSchema(); await stateModel.syncSchema(); let stateHistoryModel = new VirtualStateHistoryModel(this.DATABASE_NAME); await stateHistoryModel.defineSchema(); await stateHistoryModel.syncSchema(); }catch(e){ throw e; } } async create(userRef: string, idType: string, dbTxnId?: string): Promise { if (!userRef || !idType) { throw new LogAndError("Invalid user reference or id type in virtual state machine"); } if (!this.DATABASE_NAME) { throw new LogAndError("Invalid dase name in virtual machine"); } try { let virtualId: string = UUID.v4(); let registryModel = new VirtualRegistryModel(this.DATABASE_NAME); await registryModel.defineSchema(); let regBody: IVirtualRegistryBody = { userRef: userRef , idType: idType , createdAt: new Date() } await registryModel.create(virtualId, {registry: regBody}, dbTxnId); let stateModel = new VirtualStateModel(this.DATABASE_NAME); await stateModel.defineSchema(); let state = EVirtualState.PENDING; await stateModel.upsert(virtualId, {state: {status: state}}, dbTxnId); let stateHistoryModel = new VirtualStateHistoryModel(this.DATABASE_NAME); await stateHistoryModel.defineSchema(); await stateHistoryModel.create(UUID.v4(), {status: state}, dbTxnId); return virtualId } catch (e) { throw e; } } async getCurrentState(virtualId: string, dbTxnId?: string): Promise { if (!virtualId) { throw new LogAndError("Invalid virtual id in state mchine"); } if (!this.DATABASE_NAME) { throw new LogAndError("Invalid db name in state mchine"); } try { let stateModel = new VirtualStateModel(this.DATABASE_NAME); await stateModel.defineSchema(); if(dbTxnId) { let localDoc = await stateModel.findLocal(virtualId, dbTxnId); if (localDoc && localDoc.body && localDoc.body.state) { if(!localDoc.body.state) { throw new LogAndError("Invalid State Detail in state mchaine"); } return localDoc.body.state.status; } } let inv = await stateModel.find(virtualId, dbTxnId); if(!inv.state) { throw new LogAndError("Invalid state details in state machine"); } return inv.state.status; } catch (e) { throw e; } } async removed(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtual id in state machine of remove behaviour"); } try { await this.setStatus(virtualId, EVirtualState.REMOVED, dbTxnId); } catch (e) { throw e; } } async rejected(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtual id in state machine of reject behaviour"); } try { await this.setStatus(virtualId, EVirtualState.REJECTED, dbTxnId); } catch (e) { throw e; } } async submit(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtula id in state machine of submit behaviour"); } try { await this.setStatus(virtualId, EVirtualState.SUBMITTED, dbTxnId); } catch (e) { throw e; } } async approve(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtula id in state machine of approve behaviourt"); } try { await this.setStatus(virtualId, EVirtualState.APPROVED, dbTxnId); } catch (e) { throw e; } } async expire(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtula id in state machine of expire behaviour"); } try { await this.setStatus(virtualId, EVirtualState.EXPIRED, dbTxnId); } catch (e) { throw e; } } async revoke(virtualId: string, dbTxnId?: string) { if (!virtualId) { throw new LogAndError("Invalid virtula id in state machine of revoke behaviour"); } try { await this.setStatus(virtualId, EVirtualState.REVOKED, dbTxnId); } catch (e) { throw e; } } private async setStatus(virtualId: string, toState: EVirtualState, dbTxnId?: string) { if (!virtualId || !toState) { throw new LogAndError("Invalid virtula id or virtual state in state machine of setStatus behaviour"); } if (!this.DATABASE_NAME) { throw new LogAndError("Invalid db name"); } try { let curState = await this.getCurrentState(virtualId, dbTxnId); if (!curState) { throw new LogAndError("Could not get current State in state machine of setStatus behaviour"); } await VirtualStatusCheck.checkForStatusChange(toState, curState) let stateModel = new VirtualStateModel(this.DATABASE_NAME); await stateModel.defineSchema(); await stateModel.upsert(virtualId, {state: {status: toState}}, dbTxnId); let stateHistoryModel = new VirtualStateHistoryModel(this.DATABASE_NAME); await stateHistoryModel.defineSchema(); await stateHistoryModel.create(UUID.v4(), {status: toState}, dbTxnId); } catch (e) { throw e; } } }