Improved account transfer UI, added config-based api switch.
This commit is contained in:
		
							parent
							
								
									506a474819
								
							
						
					
					
						commit
						db2bcb576f
					
				| 
						 | 
					@ -1,7 +1,10 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.api;
 | 
					package nl.andrewl.coyotecredit.ctl.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.ctl.exchange.dto.PublicAccountData;
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.model.User;
 | 
				
			||||||
import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
					import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
				
			||||||
 | 
					import org.springframework.security.core.annotation.AuthenticationPrincipal;
 | 
				
			||||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
					import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
					import org.springframework.web.bind.annotation.PathVariable;
 | 
				
			||||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
					import org.springframework.web.bind.annotation.RequestMapping;
 | 
				
			||||||
| 
						 | 
					@ -16,7 +19,12 @@ public class ExchangeApiController {
 | 
				
			||||||
	private final ExchangeService exchangeService;
 | 
						private final ExchangeService exchangeService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@GetMapping(path = "/tradeables")
 | 
						@GetMapping(path = "/tradeables")
 | 
				
			||||||
	public Map<Long, String> getCurrentTradeables(@PathVariable long exchangeId) {
 | 
						public Map<Long, String> getCurrentTradeables(@PathVariable long exchangeId, @AuthenticationPrincipal User user) {
 | 
				
			||||||
		return exchangeService.getCurrentTradeables(exchangeId);
 | 
							return exchangeService.getCurrentTradeables(exchangeId, user);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@GetMapping(path = "/accounts/{number}")
 | 
				
			||||||
 | 
						public PublicAccountData getAccountData(@PathVariable long exchangeId, @PathVariable String number, @AuthenticationPrincipal User user) {
 | 
				
			||||||
 | 
							return exchangeService.getPublicAccountData(exchangeId, number, user);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.TransferPayload;
 | 
					import nl.andrewl.coyotecredit.ctl.exchange.dto.TransferPayload;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.User;
 | 
					import nl.andrewl.coyotecredit.model.User;
 | 
				
			||||||
import nl.andrewl.coyotecredit.service.AccountService;
 | 
					import nl.andrewl.coyotecredit.service.AccountService;
 | 
				
			||||||
import org.springframework.http.HttpStatus;
 | 
					import org.springframework.http.HttpStatus;
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ public class AccountPage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@GetMapping(path = "/transfer")
 | 
						@GetMapping(path = "/transfer")
 | 
				
			||||||
	public String getTransferPage(Model model, @PathVariable long accountId, @AuthenticationPrincipal User user) {
 | 
						public String getTransferPage(Model model, @PathVariable long accountId, @AuthenticationPrincipal User user) {
 | 
				
			||||||
		model.addAttribute("balances", accountService.getTransferData(accountId, user));
 | 
							model.addAttribute("data", accountService.getTransferData(accountId, user));
 | 
				
			||||||
		return "account/transfer";
 | 
							return "account/transfer";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.TradePayload;
 | 
					import nl.andrewl.coyotecredit.ctl.exchange.dto.TradePayload;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.User;
 | 
					import nl.andrewl.coyotecredit.model.User;
 | 
				
			||||||
import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
					import nl.andrewl.coyotecredit.service.ExchangeService;
 | 
				
			||||||
import nl.andrewl.coyotecredit.service.TradeService;
 | 
					import nl.andrewl.coyotecredit.service.TradeService;
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ public class TradePage {
 | 
				
			||||||
			@AuthenticationPrincipal User user
 | 
								@AuthenticationPrincipal User user
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		model.addAttribute("data", tradeService.getTradeData(accountId, user));
 | 
							model.addAttribute("data", tradeService.getTradeData(accountId, user));
 | 
				
			||||||
		return "trade";
 | 
							return "account/trade";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@PostMapping
 | 
						@PostMapping
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public record BalanceData(
 | 
					public record BalanceData(
 | 
				
			||||||
	long id,
 | 
						long id,
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,5 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.SimpleAccountData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public record ExchangeAccountData(
 | 
					public record ExchangeAccountData(
 | 
				
			||||||
		ExchangeData exchange,
 | 
							ExchangeData exchange,
 | 
				
			||||||
		SimpleAccountData account
 | 
							SimpleAccountData account
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,4 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.ExchangeData;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Account data that can be publicly seen by any member of an exchange.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public record PublicAccountData(
 | 
				
			||||||
 | 
							long id,
 | 
				
			||||||
 | 
							String number,
 | 
				
			||||||
 | 
							String name
 | 
				
			||||||
 | 
					) {}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public record SimpleAccountData (
 | 
					public record SimpleAccountData (
 | 
				
			||||||
		long id,
 | 
							long id,
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.ctl.dto.TradeableData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.math.BigDecimal;
 | 
					import java.math.BigDecimal;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The payload that's sent when a user performs a trade with the market. This
 | 
					 * The payload that's sent when a user performs a trade with the market. This
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.ctl.dto.TradeableData;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.Transaction;
 | 
					import nl.andrewl.coyotecredit.model.Transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.time.format.DateTimeFormatter;
 | 
					import java.time.format.DateTimeFormatter;
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import nl.andrewl.coyotecredit.ctl.dto.TradeableData;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.Transfer;
 | 
					import nl.andrewl.coyotecredit.model.Transfer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.time.format.DateTimeFormatter;
 | 
					import java.time.format.DateTimeFormatter;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public record TransferPageData(
 | 
				
			||||||
 | 
							long exchangeId,
 | 
				
			||||||
 | 
							List<BalanceData> balances
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.ctl.dto;
 | 
					package nl.andrewl.coyotecredit.ctl.exchange.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public record TransferPayload (
 | 
					public record TransferPayload (
 | 
				
			||||||
		String recipientNumber,
 | 
							String recipientNumber,
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ public interface AccountRepository extends JpaRepository<Account, Long> {
 | 
				
			||||||
	List<Account> findAllByUser(User user);
 | 
						List<Account> findAllByUser(User user);
 | 
				
			||||||
	Optional<Account> findByNumber(String number);
 | 
						Optional<Account> findByNumber(String number);
 | 
				
			||||||
	Optional<Account> findByUserAndExchange(User user, Exchange exchange);
 | 
						Optional<Account> findByUserAndExchange(User user, Exchange exchange);
 | 
				
			||||||
 | 
						Optional<Account> findByNumberAndExchange(String number, Exchange exchange);
 | 
				
			||||||
	boolean existsByUserAndExchange(User user, Exchange exchange);
 | 
						boolean existsByUserAndExchange(User user, Exchange exchange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	List<Account> findAllByExchange(Exchange e);
 | 
						List<Account> findAllByExchange(Exchange e);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ package nl.andrewl.coyotecredit.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.*;
 | 
					import nl.andrewl.coyotecredit.ctl.dto.*;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.exchange.dto.ExchangeData;
 | 
					import nl.andrewl.coyotecredit.ctl.exchange.dto.*;
 | 
				
			||||||
import nl.andrewl.coyotecredit.dao.*;
 | 
					import nl.andrewl.coyotecredit.dao.*;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.*;
 | 
					import nl.andrewl.coyotecredit.model.*;
 | 
				
			||||||
import nl.andrewl.coyotecredit.util.AccountNumberUtils;
 | 
					import nl.andrewl.coyotecredit.util.AccountNumberUtils;
 | 
				
			||||||
| 
						 | 
					@ -34,13 +34,13 @@ public class AccountService {
 | 
				
			||||||
	private final UserNotificationRepository notificationRepository;
 | 
						private final UserNotificationRepository notificationRepository;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional(readOnly = true)
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
	public List<BalanceData> getTransferData(long accountId, User user) {
 | 
						public TransferPageData getTransferData(long accountId, User user) {
 | 
				
			||||||
		Account account = accountRepository.findById(accountId)
 | 
							Account account = accountRepository.findById(accountId)
 | 
				
			||||||
				.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
				
			||||||
		if (!account.getUser().getId().equals(user.getId())) {
 | 
							if (!account.getUser().getId().equals(user.getId())) {
 | 
				
			||||||
			throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
								throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return account.getBalances().stream()
 | 
							List<BalanceData> balances = account.getBalances().stream()
 | 
				
			||||||
				.filter(b -> b.getAmount().compareTo(BigDecimal.ZERO) > 0)
 | 
									.filter(b -> b.getAmount().compareTo(BigDecimal.ZERO) > 0)
 | 
				
			||||||
				.map(b -> new BalanceData(
 | 
									.map(b -> new BalanceData(
 | 
				
			||||||
						b.getTradeable().getId(),
 | 
											b.getTradeable().getId(),
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,7 @@ public class AccountService {
 | 
				
			||||||
				))
 | 
									))
 | 
				
			||||||
				.sorted(Comparator.comparing(BalanceData::symbol))
 | 
									.sorted(Comparator.comparing(BalanceData::symbol))
 | 
				
			||||||
				.toList();
 | 
									.toList();
 | 
				
			||||||
 | 
							return new TransferPageData(account.getExchange().getId(), balances);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional
 | 
						@Transactional
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +103,18 @@ public class ExchangeService {
 | 
				
			||||||
				.toList();
 | 
									.toList();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
 | 
						public PublicAccountData getPublicAccountData(long exchangeId, String number, User user) {
 | 
				
			||||||
 | 
							Exchange exchange = exchangeRepository.findById(exchangeId)
 | 
				
			||||||
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
				
			||||||
 | 
							if (!accountRepository.existsByUserAndExchange(user, exchange)) {
 | 
				
			||||||
 | 
								throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							Account account = accountRepository.findByNumberAndExchange(number, exchange)
 | 
				
			||||||
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
				
			||||||
 | 
							return new PublicAccountData(account.getId(), account.getNumber(), account.getName());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional(readOnly = true)
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
	public void ensureAdminAccount(long exchangeId, User user) {
 | 
						public void ensureAdminAccount(long exchangeId, User user) {
 | 
				
			||||||
		getExchangeIfAdmin(exchangeId, user);
 | 
							getExchangeIfAdmin(exchangeId, user);
 | 
				
			||||||
| 
						 | 
					@ -168,9 +180,12 @@ public class ExchangeService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Transactional(readOnly = true)
 | 
						@Transactional(readOnly = true)
 | 
				
			||||||
	public Map<Long, String> getCurrentTradeables(long exchangeId) {
 | 
						public Map<Long, String> getCurrentTradeables(long exchangeId, User user) {
 | 
				
			||||||
		Exchange e = exchangeRepository.findById(exchangeId)
 | 
							Exchange e = exchangeRepository.findById(exchangeId)
 | 
				
			||||||
				.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
									.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
 | 
				
			||||||
 | 
							if (!accountRepository.existsByUserAndExchange(user, e)) {
 | 
				
			||||||
 | 
								throw new ResponseStatusException(HttpStatus.NOT_FOUND);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		Map<Long, String> tradeables = new HashMap<>();
 | 
							Map<Long, String> tradeables = new HashMap<>();
 | 
				
			||||||
		for (var t : e.getAllTradeables()) {
 | 
							for (var t : e.getAllTradeables()) {
 | 
				
			||||||
			tradeables.put(t.getId(), t.getMarketPriceUsd().toPlainString());
 | 
								tradeables.put(t.getId(), t.getMarketPriceUsd().toPlainString());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package nl.andrewl.coyotecredit.service;
 | 
					package nl.andrewl.coyotecredit.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.TradeData;
 | 
					import nl.andrewl.coyotecredit.ctl.exchange.dto.TradeData;
 | 
				
			||||||
import nl.andrewl.coyotecredit.ctl.dto.TradeableData;
 | 
					import nl.andrewl.coyotecredit.ctl.dto.TradeableData;
 | 
				
			||||||
import nl.andrewl.coyotecredit.dao.AccountRepository;
 | 
					import nl.andrewl.coyotecredit.dao.AccountRepository;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.Account;
 | 
					import nl.andrewl.coyotecredit.model.Account;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import nl.andrewl.coyotecredit.dao.TradeableRepository;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.Tradeable;
 | 
					import nl.andrewl.coyotecredit.model.Tradeable;
 | 
				
			||||||
import nl.andrewl.coyotecredit.model.TradeableType;
 | 
					import nl.andrewl.coyotecredit.model.TradeableType;
 | 
				
			||||||
import org.springframework.beans.factory.annotation.Value;
 | 
					import org.springframework.beans.factory.annotation.Value;
 | 
				
			||||||
 | 
					import org.springframework.core.env.Environment;
 | 
				
			||||||
import org.springframework.scheduling.annotation.Scheduled;
 | 
					import org.springframework.scheduling.annotation.Scheduled;
 | 
				
			||||||
import org.springframework.stereotype.Service;
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit;
 | 
				
			||||||
@Slf4j
 | 
					@Slf4j
 | 
				
			||||||
public class TradeableUpdateService {
 | 
					public class TradeableUpdateService {
 | 
				
			||||||
	private final TradeableRepository tradeableRepository;
 | 
						private final TradeableRepository tradeableRepository;
 | 
				
			||||||
 | 
						private final Environment environment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private final HttpClient httpClient = HttpClient.newHttpClient();
 | 
						private final HttpClient httpClient = HttpClient.newHttpClient();
 | 
				
			||||||
	private final ObjectMapper objectMapper = new ObjectMapper();
 | 
						private final ObjectMapper objectMapper = new ObjectMapper();
 | 
				
			||||||
| 
						 | 
					@ -57,7 +59,11 @@ public class TradeableUpdateService {
 | 
				
			||||||
		for (var tradeable : publicTradeables) {
 | 
							for (var tradeable : publicTradeables) {
 | 
				
			||||||
			// Special case of ignoring USD as the universal transfer currency.
 | 
								// Special case of ignoring USD as the universal transfer currency.
 | 
				
			||||||
			if (tradeable.getSymbol().equals("USD")) continue;
 | 
								if (tradeable.getSymbol().equals("USD")) continue;
 | 
				
			||||||
			executorService.schedule(() -> updateTradeable(tradeable), delay, TimeUnit.SECONDS);
 | 
								if (environment.getProperty("coyote-credit.enable-tradeable-updates", Boolean.TYPE, true)) {
 | 
				
			||||||
 | 
									executorService.schedule(() -> updateTradeable(tradeable), delay, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									log.info("Tradeable update skipped for {}.", tradeable.getSymbol());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			delay += POLYGON_API_TIMEOUT;
 | 
								delay += POLYGON_API_TIMEOUT;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,3 +10,4 @@ spring.mail.host=127.0.0.1
 | 
				
			||||||
spring.mail.port=1025
 | 
					spring.mail.port=1025
 | 
				
			||||||
 | 
					
 | 
				
			||||||
coyote-credit.base-url=http://localhost:8080
 | 
					coyote-credit.base-url=http://localhost:8080
 | 
				
			||||||
 | 
					coyote-credit.enable-tradeable-updates=false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,14 @@
 | 
				
			||||||
const tradeableSelect = document.getElementById("tradeableSelect");
 | 
					const tradeableSelect = document.getElementById("tradeableSelect");
 | 
				
			||||||
const valueInput = document.getElementById("amountInput");
 | 
					const valueInput = document.getElementById("amountInput");
 | 
				
			||||||
 | 
					const recipientNumberInput = document.getElementById("recipientNumberInput");
 | 
				
			||||||
 | 
					const recipientNumberNote = document.getElementById("recipientNumberNote");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const exchangeId = document.getElementById("exchangeIdInput").value;
 | 
				
			||||||
 | 
					const accountNumberRegex = new RegExp("\\d{4}-\\d{4}-\\d{4}-\\d{4}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tradeableSelect.addEventListener("change", onSelectChanged);
 | 
					tradeableSelect.addEventListener("change", onSelectChanged);
 | 
				
			||||||
 | 
					recipientNumberInput.addEventListener("change", onAccountNumberChanged);
 | 
				
			||||||
 | 
					recipientNumberInput.addEventListener("keyup", onAccountNumberChanged);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onSelectChanged() {
 | 
					function onSelectChanged() {
 | 
				
			||||||
    valueInput.value = null;
 | 
					    valueInput.value = null;
 | 
				
			||||||
| 
						 | 
					@ -17,3 +24,52 @@ function onSelectChanged() {
 | 
				
			||||||
        valueInput.setAttribute("min", 0.0000000001);
 | 
					        valueInput.setAttribute("min", 0.0000000001);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Check that the account number that was entered is valid.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function onAccountNumberChanged() {
 | 
				
			||||||
 | 
					    const currentNumber = recipientNumberInput.value;
 | 
				
			||||||
 | 
					    console.log("Account number changed to " + currentNumber);
 | 
				
			||||||
 | 
					    if (currentNumber !== undefined && currentNumber.length > 0) {
 | 
				
			||||||
 | 
					        if (accountNumberRegex.test(currentNumber)) {
 | 
				
			||||||
 | 
					            recipientNumberNote.innerText = "Valid account number.";
 | 
				
			||||||
 | 
					            fetch("/api/exchanges/" + exchangeId + "/accounts/" + currentNumber)
 | 
				
			||||||
 | 
					                .then(response => {
 | 
				
			||||||
 | 
					                    if (response.status === 200) {
 | 
				
			||||||
 | 
					                        response.json()
 | 
				
			||||||
 | 
					                            .then(data => {
 | 
				
			||||||
 | 
					                                console.log(data);
 | 
				
			||||||
 | 
					                                showRecipientNumberNote("info", "Account number is valid. Will transfer to <span class='fw-bold'>" + data.name + "</span>");
 | 
				
			||||||
 | 
					                            })
 | 
				
			||||||
 | 
					                            .catch(() => {
 | 
				
			||||||
 | 
					                                showRecipientNumberNote("warning", "Could not find an account with that number.");
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                    } else if (response.status === 404) {
 | 
				
			||||||
 | 
					                        showRecipientNumberNote("warning", "Could not find an account with that number.");
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        showRecipientNumberNote("warning", "Error: Couldn't read API response.");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch(error => {
 | 
				
			||||||
 | 
					                    console.log(error);
 | 
				
			||||||
 | 
					                    showRecipientNumberNote("warning", "API error occurred. Couldn't fetch account information.");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            showRecipientNumberNote("warning", "Invalid account number. Format: <span class='monospace'>1234-1234-1234-1234</span>");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        showRecipientNumberNote("info", "Enter an account number.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function showRecipientNumberNote(type, text) {
 | 
				
			||||||
 | 
					    recipientNumberNote.innerHTML = text;
 | 
				
			||||||
 | 
					    if (type === "warning") {
 | 
				
			||||||
 | 
					        recipientNumberNote.classList.add("text-danger");
 | 
				
			||||||
 | 
					        recipientNumberNote.classList.remove("text-muted");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        recipientNumberNote.classList.remove("text-danger");
 | 
				
			||||||
 | 
					        recipientNumberNote.classList.add("text-muted");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,11 +10,12 @@
 | 
				
			||||||
        Transfer funds to other accounts.
 | 
					        Transfer funds to other accounts.
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
    <form th:action="@{/accounts/{aId}/transfer(aId=${accountId})}" th:method="post">
 | 
					    <form th:action="@{/accounts/{aId}/transfer(aId=${accountId})}" th:method="post">
 | 
				
			||||||
 | 
					        <input type="hidden" id="exchangeIdInput" th:value="${data.exchangeId()}"/>
 | 
				
			||||||
        <div class="mb-3">
 | 
					        <div class="mb-3">
 | 
				
			||||||
            <label for="recipientNumberInput" class="form-label">Recipient Account Number</label>
 | 
					            <label for="recipientNumberInput" class="form-label">Recipient Account Number</label>
 | 
				
			||||||
            <input type="text" name="recipientNumber" class="form-control" id="recipientNumberInput" required/>
 | 
					            <input type="text" name="recipientNumber" class="form-control monospace" id="recipientNumberInput" required/>
 | 
				
			||||||
            <small class="text-muted">
 | 
					            <small class="text-muted text-danger" id="recipientNumberNote">
 | 
				
			||||||
                Format: <span class="monospace">1234-1234-1234-1234</span><br>
 | 
					                Enter an account number.
 | 
				
			||||||
            </small>
 | 
					            </small>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="mb-3">
 | 
					        <div class="mb-3">
 | 
				
			||||||
| 
						 | 
					@ -22,7 +23,7 @@
 | 
				
			||||||
            <select class="form-select" id="tradeableSelect" name="tradeableId" required>
 | 
					            <select class="form-select" id="tradeableSelect" name="tradeableId" required>
 | 
				
			||||||
                <option value="" selected disabled hidden>Choose something to send</option>
 | 
					                <option value="" selected disabled hidden>Choose something to send</option>
 | 
				
			||||||
                <option
 | 
					                <option
 | 
				
			||||||
                        th:each="b : ${balances}"
 | 
					                        th:each="b : ${data.balances()}"
 | 
				
			||||||
                        th:text="${b.symbol() + ' - Balance ' + b.amount()}"
 | 
					                        th:text="${b.symbol() + ' - Balance ' + b.amount()}"
 | 
				
			||||||
                        th:value="${b.id()}"
 | 
					                        th:value="${b.id()}"
 | 
				
			||||||
                        th:data-amount="${b.amount()}"
 | 
					                        th:data-amount="${b.amount()}"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue