UNPKG

@accounter/server

Version:
138 lines 5.88 kB
import { __decorate, __metadata } from "tslib"; import DataLoader from 'dataloader'; import { format, subHours } from 'date-fns'; import { GraphQLError } from 'graphql'; import { Injectable, Scope } from 'graphql-modules'; import { sql } from '@pgtyped/runtime'; import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js'; import { CoinMarketCapProvider } from '../../app-providers/coinmarketcap.js'; import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js'; const getRateByCurrencyAndDate = sql ` SELECT * FROM accounter_schema.crypto_exchange_rates WHERE coin_symbol = $currency AND date = $date;`; const insertRates = sql ` INSERT INTO accounter_schema.crypto_exchange_rates (date, coin_symbol, value, against, sample_date) VALUES $$rates(date, currency, value, against, sampleDate) ON CONFLICT (date, coin_symbol, against) DO UPDATE SET value = EXCLUDED.value RETURNING *; `; const getCryptoCurrenciesBySymbol = sql ` SELECT * FROM accounter_schema.crypto_currencies WHERE symbol in $$currencySymbols;`; let CryptoExchangeProvider = class CryptoExchangeProvider { db; adminContextProvider; coinMarketCap; constructor(db, adminContextProvider, coinMarketCap) { this.db = db; this.adminContextProvider = adminContextProvider; this.coinMarketCap = coinMarketCap; } async getCryptoExchangeRatesFromDB(currency, date) { return getRateByCurrencyAndDate.run({ currency, date }, this.db); } async addRates(rates) { this.clearCache(); return insertRates.run({ rates }, this.db); } addCryptoRateLoader = new DataLoader((keys) => this.addRates(keys.map(rate => ({ ...rate, against: rate.against ?? null }))), { cache: false, }); async getCryptoCurrenciesBySymbol(symbols) { const currencies = await getCryptoCurrenciesBySymbol.run({ currencySymbols: symbols, }, this.db); return symbols.map(symbol => currencies.find(currency => currency.symbol === symbol)); } getCryptoCurrenciesBySymbolLoader = new DataLoader((symbols) => this.getCryptoCurrenciesBySymbol(symbols)); async getCryptoExchangeRatesFromAPI(currencySymbol, date) { // Fetch CoinMarketCap id from DB const currencyInfo = await this.getCryptoCurrenciesBySymbolLoader.load(currencySymbol); if (!currencyInfo) { throw new GraphQLError(`No data found for crypto currency ${currencySymbol}`); } const coinmarketcapId = currencyInfo?.coinmarketcap_id; if (!coinmarketcapId) { throw new GraphQLError(`No CoinMarketCap id found for crypto currency ${currencySymbol}`); } const fromDate = subHours(date, 23); const from = Math.floor(fromDate.getTime() / 1000); const to = Math.floor(date.getTime() / 1000); const ratesObject = await this.coinMarketCap.getExchangeRates(coinmarketcapId, { fromTimeStamp: from, toTimeStamp: to, }); let timestamp = undefined; let rate = undefined; for (const [rawTimestamp, rateData] of Object.entries(ratesObject)) { const newTimestamp = Number(rawTimestamp); if (newTimestamp <= to && rateData?.c?.[0]) { if (!timestamp || !rate) { timestamp = newTimestamp; rate = rateData.c[0]; } if (newTimestamp > timestamp) { timestamp = newTimestamp; rate = rateData.c[0]; } } } if (!rate || !timestamp) { throw new GraphQLError(`No suitable rate of ${currencySymbol} found in CoinMarketCap`); } const sampleDate = new Date(timestamp * 1000); const { defaultCryptoConversionFiatCurrency } = await this.adminContextProvider.getVerifiedAdminContext(); // Add rate to DB await this.addCryptoRateLoader.load({ date, currency: currencySymbol, value: rate, against: defaultCryptoConversionFiatCurrency, sampleDate, }); return rate; } getCryptoExchangeRateLoader = new DataLoader(async (keys) => { const { defaultCryptoConversionFiatCurrency } = await this.adminContextProvider.getVerifiedAdminContext(); const rates = await Promise.all(keys.map(async ({ cryptoCurrency, date, against = defaultCryptoConversionFiatCurrency }) => { // Fetch from DB first const res = await this.getCryptoExchangeRatesFromDB(cryptoCurrency, date); if (res.length > 0) { return res[0]; } // If not found in DB, fetch from API const rate = await this.getCryptoExchangeRatesFromAPI(cryptoCurrency, date); if (rate == null) { return new GraphQLError(`No data found for ${cryptoCurrency} on ${format(date, 'dd-MM-yyyy')}`); } return { date, coin_symbol: cryptoCurrency, value: rate.toString(), against, }; })); return rates; }, { cacheKeyFn: ({ cryptoCurrency, date, against }) => `${cryptoCurrency}-${date.getTime()}-${against}`, }); clearCache() { this.getCryptoCurrenciesBySymbolLoader.clearAll(); this.getCryptoExchangeRateLoader.clearAll(); } }; CryptoExchangeProvider = __decorate([ Injectable({ scope: Scope.Operation, global: true, }), __metadata("design:paramtypes", [TenantAwareDBClient, AdminContextProvider, CoinMarketCapProvider]) ], CryptoExchangeProvider); export { CryptoExchangeProvider }; //# sourceMappingURL=crypto-exchange.provider.js.map