diff --git a/src/ain.ts b/src/ain.ts index ac5eb05..18b58d3 100644 --- a/src/ain.ts +++ b/src/ain.ts @@ -55,4 +55,9 @@ export default class AinModule { this.isDefaultAccountExist(); return this.ain!.wallet.defaultAccount!.address; } + + getEventManager() { + this.checkAinInitiated(); + return this.ain!.em; + } } \ No newline at end of file diff --git a/src/ainize.ts b/src/ainize.ts index c66ab5f..2b24fe1 100644 --- a/src/ainize.ts +++ b/src/ainize.ts @@ -11,7 +11,6 @@ export default class Ainize { private cache: NodeCache; ain: Ain; middleware: Middleware; - handler: Handler; appController: AppController = AppController.getInstance(); constructor(chainId: 1 | 0) { @@ -19,7 +18,6 @@ export default class Ainize { this.ain = new Ain(blockChainEndpoint, chainId); this.cache = new NodeCache(); this.middleware = new Middleware(this.cache); - this.handler = new Handler(this); } // FIXME(yoojin): add config type and change param type. diff --git a/src/controllers/modelController.ts b/src/controllers/modelController.ts index 94e9f10..6c5bb3f 100644 --- a/src/controllers/modelController.ts +++ b/src/controllers/modelController.ts @@ -3,14 +3,15 @@ import AinModule from "../ain"; import { Path } from "../constants"; import { getRequestDepositOp, getTransferOp } from "../utils/operator"; import { buildSetOperation, buildTxBody } from "../utils/builder"; +import Handler from "../handlers/handler"; export default class ModelController { private static instance: ModelController | undefined; private ain = AinModule.getInstance(); + private handler = Handler.getInstance(); static getInstance() { if(!ModelController.instance){ ModelController.instance = new ModelController(); - } return ModelController.instance; } @@ -73,13 +74,17 @@ export default class ModelController { //TODO(woojae): connect with handler async use(modelName: string, requestData: string) { this.isLoggedIn(); - const requestKey = Date.now(); - const requesterAddress = this.ain.getAddress(); - const requestPath = Path.app(modelName).request(requesterAddress, requestKey); - const requestOp = buildSetOperation("SET_VALUE", requestPath, {prompt: requestData}); - const txBody = buildTxBody(requestOp); - await this.ain.sendTransaction(txBody); - return requestKey; + const result = await new Promise(async (resolve, reject) => { + const requestKey = Date.now(); + const requesterAddress = this.ain.getAddress(); + await this.handler.subscribe(requesterAddress, requestKey.toString(), modelName, resolve); + const requestPath = Path.app(modelName).request(requesterAddress, requestKey); + const requestOp = buildSetOperation("SET_VALUE", requestPath, {prompt: requestData}); + const txBody = buildTxBody(requestOp); + await this.ain.sendTransaction(txBody); + return requestKey; + }); + return result; } //TODO(woojae): implement this diff --git a/src/handlers/handler.ts b/src/handlers/handler.ts index 725f1e6..9a8c2cd 100644 --- a/src/handlers/handler.ts +++ b/src/handlers/handler.ts @@ -1,94 +1,67 @@ const _ = require("lodash"); import Ain from "@ainblockchain/ain-js"; -import { HANDLER_HEARBEAT_INTERVAL, Path } from "../constants"; import Ainize from "../ainize"; +import { Path } from "../constants"; +import AinModule from "../ain"; +import EventManager from "@ainblockchain/ain-js/lib/event-manager"; export default class Handler { - isConnected: boolean = false; - subscribeTable:any = {}; - ain: Ain; - constructor(ainize: Ainize) { - this.ain = ainize.ain; + private static instance: Handler | undefined; + em = AinModule.getInstance().getEventManager(); + static getInstance() { + if(!Handler.instance){ + Handler.instance = new Handler(); + } + return Handler.instance; + } + + checkEventManager() { + if (!this.em) { + if(!AinModule.getInstance().getEventManager()){ + throw new Error('you should init ain first'); + } + this.em = AinModule.getInstance().getEventManager(); + } + return true; } - /** - * Connect to ai Network event node. you should connect before subscibe. It will auto reconnect when disconnected. - * @returns Nothing. - */ async connect() { - await this.ain.em.connect({}, this.disconnectedCallback.bind(this)); - this.isConnected = true; + this.checkEventManager(); + await this.em!.connect({},this.disconnectedCb); + console.log('connected'); }; - private async disconnectedCallback() { - this.isConnected = false; + private async disconnectedCb() { + console.log('disconnected. reconnecting...'); await this.connect(); } - /** - * Subscribe to specific service reponse. You can handle reponse with callback function. - * You should connect before subscibe. - * @param {string} userAddress - Address of account you request with. - * @param {string} appName - App name you want to subscribe. - * @param {Function(valueChangedEvent: any)} callback - A callback function to handle response. It will be called when response is written. - * @returns SubscribeId. - */ - async subscribe(userAddress:string, appName: string, callback: (valueChangedEvent: any) => any) { - if (this.checkSubscribeTableExists(userAddress, appName)){ - throw new Error("Already subscribed"); - } - const subscribeId = await this.ain.em.subscribe( + async subscribe(requester:string, recordId:string, appName: string, resolve: any) { + this.checkEventManager(); + const responsePath = Path.app(appName).response(requester, recordId); + const subscribeId = await this.em!.subscribe( "VALUE_CHANGED", { - path: Path.app(appName).response(userAddress, "$requestKey"), + path: responsePath, event_source: "USER", }, - (valueChangedEvent) => { - callback(valueChangedEvent); + (valueChangedEvent: any) => { + this.unsubscribe(subscribeId); + resolve(valueChangedEvent.values.after.data); }, (err) => { throw new Error(err.message); }, ); - this.addToSubscribeTable(userAddress, appName, subscribeId); - return subscribeId; - } - - private checkSubscribeTableExists(userAddress:string, appName:string,) { - return _.has(this.subscribeTable, [userAddress, appName]); - } - - private addToSubscribeTable(userAddress:string, appName: string, filterId: string) { - _.set(this.subscribeTable, [userAddress], {appName:filterId}); } - /** - * Get subscribe list of userAddress. If you don't set userAddress, it will return all subscribe list. - * @param {string=} userAddress - Address of account you want to get subscribe list. - * @returns Result of transaction. - */ - getSubscribeList(userAddress?: string) { - if (!userAddress) return this.subscribeTable; - return this.subscribeTable[userAddress]; - } - /** - * Unsubscribe to specific service reponse. - * @param {string} userAddress - Address of account you want to unsubscribe. - * @param {string} appName - App name you want to unsubscribe. - * @returns True if successfuly unsubscribed. - */ - unsubscribe(userAddress:string, appName: string) { - if (!this.checkSubscribeTableExists(userAddress, appName)) { - throw new Error("Not subscribed"); - } - this.ain.em.unsubscribe( - this.subscribeTable[userAddress][appName], + async unsubscribe(filterId: string) { + this.checkEventManager(); + await this.em!.unsubscribe( + filterId, (err)=>{ if (err) { throw new Error(err.message); - } else { - this.subscribeTable[userAddress][appName] = null; - return true; } }); } diff --git a/src/modules/moduleBase.ts b/src/modules/moduleBase.ts deleted file mode 100644 index 9767d7f..0000000 --- a/src/modules/moduleBase.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Ain from "@ainblockchain/ain-js"; -import { SetOperation, TransactionBody } from "@ainblockchain/ain-js/lib/types"; -import Ainize from "../ainize"; -import { opResult, txResult } from "../types/type"; - -export default class ModuleBase { - protected ain: Ain; - constructor(ainize: Ainize) { - this.ain = ainize.ain; - } - - protected getDefaultAccount() { - const defaultAccount = this.ain.wallet.defaultAccount; - if (!defaultAccount) - throw new Error("You need to set default account."); - return defaultAccount; - } - - private async _sendTransaction(txBody: TransactionBody) { - return await this.ain.sendTransaction(txBody); - } - - private hasFailedOpResultList(result: txResult): boolean { - if (result.result_list) { - return Object.values(result.result_list).some( - (result: { code: number }) => result.code !== 0 - ); - } - return result.code !== 0; - } - - private handleTxResultWrapper(operation: Function) { - return async (args: any) => { - const res = await operation(args); - const { tx_hash, result } = res; - if (this.hasFailedOpResultList(result)) { - throw new Error( - `Failed to send transaction (${tx_hash}).\n Tx Result: ${JSON.stringify(result)}` - ); - } - return tx_hash; - } - } - - protected sendTransaction = this.handleTxResultWrapper(this._sendTransaction.bind(this)); -}