UNPKG

futoin-xferengine

Version:

Neutral clustered multi-currency transaction engine with limits

275 lines (237 loc) 8.63 kB
'use strict'; /** * @file * * Copyright 2017 FutoIn Project (https://futoin.org) * Copyright 2017 Andrey Galkin <andrey@futoin.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const AdvancedCCM = require( 'futoin-invoker/AdvancedCCM' ); const DepositFace = require( './DepositFace' ); const WithdrawFace = require( './WithdrawFace' ); const GamingFace = require( './GamingFace' ); const PeerFace = require( './PeerFace' ); const { FTN19_VERSION, } = require( './main' ); /** * Special CCM implementation for XferCore */ class XferCCM extends AdvancedCCM { constructor( ...args ) { super( ...args ); this._on_demand_callbacks = {}; this.registerOnDemand( DepositFace.IFACE_NAME, 'default', ( as, ccm, alias, api ) => { DepositFace.register( as, ccm, alias, api.endpoint, api.credentials, api.options ); } ); this.registerOnDemand( GamingFace.IFACE_NAME, 'default', ( as, ccm, alias, api ) => { GamingFace.register( as, ccm, alias, api.endpoint, api.credentials, api.options ); } ); this.registerOnDemand( PeerFace.IFACE_NAME, 'default', ( as, ccm, alias, api ) => { PeerFace.register( as, ccm, alias, api.endpoint, api.credentials, api.options ); } ); this.registerOnDemand( WithdrawFace.IFACE_NAME, 'default', ( as, ccm, alias, api ) => { WithdrawFace.register( as, ccm, alias, api.endpoint, api.credentials, api.options ); } ); } /** * Register all services required for operation * @param {AsyncSteps} as - async step interface * @param {Executor} executor - internal protected executor */ registerServices( as, executor ) { this.registerEventServices( as, executor ); this.registerCurrencyServices( as, executor ); this.registerLimitServices( as, executor ); this.registerAccountServices( as, executor ); } /** * Register event services required for operation * @param {AsyncSteps} as - async step interface * @param {Executor} executor - internal protected executor */ registerEventServices( as, executor ) { const DBGenFace = require( 'futoin-eventstream/DBGenFace' ); const DBGenService = require( 'futoin-eventstream/DBGenService' ); DBGenService.register( as, executor ); DBGenFace.register( as, this, 'xfer.evtgen', executor ); } /** * Register currency services required for operation * @param {AsyncSteps} as - async step interface * @param {Executor} executor - internal protected executor */ registerCurrencyServices( as, executor ) { const InfoFace = require( './Currency/InfoFace' ); const InfoService = require( './Currency/InfoService' ); const ManageFace = require( './Currency/ManageFace' ); const ManageService = require( './Currency/ManageService' ); InfoService.register( as, executor ); InfoFace.register( as, this, 'currency.info', executor ); ManageService.register( as, executor ); ManageFace.register( as, this, 'currency.manage', executor ); } /** * Register limit services required for operation * @param {AsyncSteps} as - async step interface * @param {Executor} executor - internal protected executor */ registerLimitServices( as, executor ) { const LimitsFace = require( './LimitsFace' ); const LimitsService = require( './LimitsService' ); LimitsService.register( as, executor ); LimitsFace.register( as, this, 'xfer.limits', executor ); } /** * Register account services required for operation * @param {AsyncSteps} as - async step interface * @param {Executor} executor - internal protected executor */ registerAccountServices( as, executor ) { const AccountsFace = require( './AccountsFace' ); const AccountsService = require( './AccountsService' ); AccountsService.register( as, executor ); AccountsFace.register( as, this, 'xfer.accounts', executor ); } /** * Get manual alias for specific iface & key combination * @param {string} iface - interface identifier * @param {string} key - arbitrary key, typically account # * @returns {string} - manual key to be used with registerOnDemand() */ makeManualAlias( iface, key ) { return `#manual_${iface}_${key}`; } /** * Register callback for on-demand interface creation * @param {string} iface - full iface identifier * @param {string} flavour - a type of interface implementation * @param {callable} callback - callback to register interface */ registerOnDemand( iface, flavour, callback ) { const on_demand_cbs = this._on_demand_callbacks; on_demand_cbs[iface] = on_demand_cbs[iface] || {}; on_demand_cbs[iface][flavour] = callback; } /** * Get interface with on-demand logic * @param {AsyncSteps} as - async step interface * @param {string} iface - full iface identifier * @param {string} account - related account ID */ xferIface( as, iface, account ) { //--- const manual = this.makeManualAlias( iface, account ); try { const iface_obj = this.iface( manual ); as.add( ( as ) => as.success( iface_obj ) ); return; } catch ( _ ) { // pass } //--- this._cleanup(); const alias = `#auto_${iface}_${account}`; try { const iface_obj = this.iface( alias ); this._touch( alias, iface_obj ); as.add( ( as ) => as.success( iface_obj ) ); return; } catch ( _ ) { // pass } //--- const xferacct = this.iface( 'xfer.accounts' ); xferacct.getAccount( as, account ); as.add( ( as, account_info ) => { xferacct.getAccountHolder( as, account_info.holder ); as.add( ( as, holder_info ) => as.success( { account_info, holder_info } ) ); } ); as.add( ( as, { account_info, holder_info } ) => { const api = ( holder_info.internal.api || {} )[iface]; if ( !api ) { as.error( 'XferError', `No API config for ${holder_info.id}` ); } const flavour = api.flavour || 'default'; const on_demand_cbs = this._on_demand_callbacks; const cb = ( on_demand_cbs[iface] || {} )[flavour]; if ( !cb ) { as.error( 'XferError', `No API creation callback for ${iface}/${flavour}` ); } api.iface = iface; api.version = api.version || FTN19_VERSION; api.account_info = account_info; api.holder_info = holder_info; cb( as, this, alias, api ); as.add( ( as ) => { const iface_obj = this.iface( alias ); this._touch( alias, iface_obj ); as.success( iface_obj ); } ); } ); } _touch( alias, iface ) { void alias; void iface; // no real use case yet } _cleanup() { // no real use case yet, can be implemented in derived class } } module.exports = XferCCM;