UNPKG

@mimicry/kaleidoscope

Version:

Kaleidoscope is an NPM package that conveniently aggregates responses from multiple NFT data providers.

163 lines (142 loc) 4.44 kB
import { Chain, ConsensusFilter, ConsensusMethod } from '../../../enums'; import { ConsensusMechanism, DataProviders, Value } from '../../../types'; import { none, mad } from '../../../utils/consensusFilters'; import { median, random } from '../../../utils/consensusMethods'; import { mean } from '../../../utils/consensusMethods/mean'; export class RestfulFactory { private _verbose: boolean = false; constructor(_globalConfig: any) { if (__DEV__) { console.log(`${this.getName()} Constructor`); } if (_globalConfig.verbose) { this._verbose = Boolean(_globalConfig.verbose); } if (!_globalConfig.dataProviders) { throw new Error('No providers specified.'); } } getName(): string { return this.constructor.name; } async runFactory( _dataProviders: DataProviders, _method: string, _params: any, _consensusMechanism?: ConsensusMechanism ): Promise<any> { const values: Value[] = []; const sources: any[] = []; for (const key in _dataProviders) { const _provider = _dataProviders[key]; try { const value: Value = await _provider[_method](..._params); values.push(value); sources.push({ source: _provider.getName(), value: value.amount, }); } catch (error) { // Skips Providers with methods not implemented. // Skips Providers who's apis are down, throttled, or returning invalid data. if (__DEV__) { console.error({ provider: _provider.getName(), method: _method, // @ts-ignore error: error.message, }); } } } if (values.length === 0) { throw new Error('No valid values returned from providers.'); } let finalValue; const verboseOutput: any = { method: _method, timestamp: new Date().toISOString(), }; if (values.length === 1) { finalValue = values[0]; verboseOutput['data'] = finalValue; verboseOutput['source'] = sources[0].source; } else { finalValue = this.applyConsensusMechanism(values, _consensusMechanism); verboseOutput['currencyInfo'] = finalValue.currencyInfo; verboseOutput['data'] = finalValue.amount; verboseOutput['sources'] = sources; } return this._verbose ? verboseOutput : finalValue; } protected addDataProvider(_providerName: string, _apiKey: string) { throw new Error('Method not implemented: addDataProvider()'); } protected applyConsensusMechanism( _data: Value[], _consensusMechanism: ConsensusMechanism = { filter: ConsensusFilter.NONE, method: ConsensusMethod.MEDIAN, } ): Value { const _filteredData = this._applyConsensusFilter( _data, _consensusMechanism.filter ); const _consensusValue = this._applyConsensusMethod( _filteredData, _consensusMechanism.method ); return _consensusValue; } protected getBlockchain(_chain: Chain): string { throw new Error('Method not implemented: addDataProvider()'); } protected getCorrectProviders( _dataProviders: DataProviders, _providerName?: string ): DataProviders { let dataProviders = _dataProviders; if (_providerName) { dataProviders = { [_providerName]: _dataProviders[_providerName], }; } return dataProviders; } protected initProviders(_providers: any) { for (const [_providerName, _apiKey] of Object.entries(_providers)) { this.addDataProvider(_providerName, String(_apiKey)); } } private _applyConsensusFilter( _data: Value[], _consensusFilter?: ConsensusFilter ): Value[] { switch (_consensusFilter) { case undefined: case ConsensusFilter.NONE: return none(_data); case ConsensusFilter.MAD: return mad(_data); default: throw new Error(`${_consensusFilter} is not a valid consensus filter.`); } } private _applyConsensusMethod( _data: Value[], _consensusMethod?: ConsensusMethod ): Value { switch (_consensusMethod) { case undefined: case ConsensusMethod.MEDIAN: return median(_data); case ConsensusMethod.MEAN: return mean(_data); case ConsensusMethod.RANDOM: return random(_data); default: throw new Error(`${_consensusMethod} is not a valid consensus method.`); } } }