package org.whispersystems.bithub.storage;
import com.coinbase.api.exception.CoinbaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.bithub.client.CoinbaseClient;
import org.whispersystems.bithub.client.GithubClient;
import org.whispersystems.bithub.config.RepositoryConfiguration;
import org.whispersystems.bithub.entities.Payment;
import org.whispersystems.bithub.entities.Repository;
import org.whispersystems.bithub.entities.Transaction;
import org.whispersystems.bithub.util.Badge;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import io.dropwizard.lifecycle.Managed;
public class CacheManager implements Managed {
private static final int UPDATE_FREQUENCY_MILLIS = 60 * 1000;
private final Logger logger = LoggerFactory.getLogger(CacheManager.class);
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
private final CoinbaseClient coinbaseClient;
private final GithubClient githubClient;
private final BigDecimal payoutRate;
private final List<RepositoryConfiguration> repositories;
private AtomicReference<CurrentPayment> cachedPaymentStatus;
private AtomicReference<List<Transaction>> cachedTransactions;
private AtomicReference<List<Repository>> cachedRepositories;
public CacheManager(CoinbaseClient coinbaseClient,
GithubClient githubClient,
List<RepositoryConfiguration> repositories,
BigDecimal payoutRate)
{
this.coinbaseClient = coinbaseClient;
this.githubClient = githubClient;
this.payoutRate = payoutRate;
this.repositories = repositories;
}
@Override
public void start() throws Exception {
this.cachedPaymentStatus = new AtomicReference<>(createCurrentPaymentForBalance(coinbaseClient));
this.cachedTransactions = new AtomicReference<>(createRecentTransactions(coinbaseClient));
this.cachedRepositories = new AtomicReference<>(createRepositories(githubClient, repositories));
initializeUpdates(coinbaseClient, githubClient, repositories);
}
@Override
public void stop() throws Exception {
this.executor.shutdownNow();
}
public List<Transaction> getRecentTransactions() {
return cachedTransactions.get();
}
public CurrentPayment getCurrentPaymentAmount() {
return cachedPaymentStatus.get();
}
public List<Repository> getRepositories() {
return cachedRepositories.get();
}
public void initializeUpdates(final CoinbaseClient coinbaseClient,
final GithubClient githubClient,
final List<RepositoryConfiguration> repoConfigs)
{
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
logger.warn("Running cache update...");
try {
CurrentPayment currentPayment = createCurrentPaymentForBalance(coinbaseClient);
List<Transaction> transactions = createRecentTransactions (coinbaseClient);
List<Repository> repositories = createRepositories(githubClient, repoConfigs);
cachedPaymentStatus.set(currentPayment);
cachedTransactions.set(transactions);
cachedRepositories.set(repositories);
} catch (IOException | CoinbaseException e) {
logger.warn("Failed to update badge", e);
}
}
}, UPDATE_FREQUENCY_MILLIS, UPDATE_FREQUENCY_MILLIS, TimeUnit.MILLISECONDS);
}
private List<Repository> createRepositories(GithubClient githubClient,
List<RepositoryConfiguration> configured)
{
List<Repository> repositoryList = new LinkedList<>();
for (RepositoryConfiguration repository : configured) {
repositoryList.add(githubClient.getRepository(repository.getUrl()));
}
return repositoryList;
}
private CurrentPayment createCurrentPaymentForBalance(CoinbaseClient coinbaseClient)
throws IOException, CoinbaseException
{
BigDecimal currentBalance = coinbaseClient.getAccountBalance();
BigDecimal paymentBtc = currentBalance.multiply(payoutRate);
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
BigDecimal paymentUsd = paymentBtc.multiply(exchangeRate);
paymentUsd = paymentUsd.setScale(2, RoundingMode.CEILING);
return new CurrentPayment(Badge.createFor(paymentUsd.toPlainString()),
Badge.createSmallFor(paymentUsd.toPlainString()),
new Payment(paymentUsd.toPlainString()));
}
private List<Transaction> createRecentTransactions(CoinbaseClient coinbaseClient)
throws IOException, CoinbaseException
{
List<com.coinbase.api.entity.Transaction> recentTransactions = coinbaseClient.getRecentTransactions();
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
List<Transaction> transactions = new LinkedList<>();
for (com.coinbase.api.entity.Transaction coinbaseTransaction : recentTransactions) {
try {
if (isSentTransaction(coinbaseTransaction)) {
CoinbaseTransactionParser parser = new CoinbaseTransactionParser(coinbaseTransaction);
String url = parser.parseUrlFromMessage();
String sha = parser.parseShaFromUrl(url);
String description = githubClient.getCommitDescription(url);
transactions.add(new Transaction(parser.parseDestinationFromMessage(),
parser.parseAmountInDollars(exchangeRate),
url, sha, parser.parseTimestamp(),
description));
if (transactions.size() >= 10)
break;
}
} catch (ParseException e) {
logger.warn("Parse", e);
}
}
return transactions;
}
private boolean isSentTransaction(com.coinbase.api.entity.Transaction coinbaseTransaction) {
BigDecimal amount = coinbaseTransaction.getAmount().getAmount();
return amount.compareTo(new BigDecimal(0.0)) < 0;
}
}