Add filtering by foreign blockchain to API crosschain calls

This commit is contained in:
catbref 2020-12-10 14:52:06 +00:00
parent 456bb3ca63
commit 31fa916156
5 changed files with 59 additions and 12 deletions

View File

@ -27,7 +27,7 @@ public class TradeBotCreateRequest {
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
public Long bitcoinAmount; public Long bitcoinAmount;
@Schema(description = "Foreign blockchain. Note: default (BITCOIN) to be removed in the future", example = "BITCOIN", defaultValue = "BITCOIN") @Schema(description = "Foreign blockchain. Note: default (BITCOIN) to be removed in the future", example = "BITCOIN", implementation = SupportedBlockchain.class)
public SupportedBlockchain foreignBlockchain; public SupportedBlockchain foreignBlockchain;
@Schema(description = "Foreign blockchain amount wanted in return", example = "0.00864200", type = "number") @Schema(description = "Foreign blockchain amount wanted in return", example = "0.00864200", type = "number")

View File

@ -80,7 +80,11 @@ public class CrossChainResource {
) )
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE}) @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE})
public List<CrossChainTradeData> getTradeOffers( public List<CrossChainTradeData> getTradeOffers(
// TODO: we need a param to limit to specific foreign blockchain(s) @Parameter(
description = "Limit to specific blockchain",
example = "LITECOIN",
schema = @Schema(implementation = SupportedBlockchain.class)
) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain,
@Parameter( ref = "limit") @QueryParam("limit") Integer limit, @Parameter( ref = "limit") @QueryParam("limit") Integer limit,
@Parameter( ref = "offset" ) @QueryParam("offset") Integer offset, @Parameter( ref = "offset" ) @QueryParam("offset") Integer offset,
@Parameter( ref = "reverse" ) @QueryParam("reverse") Boolean reverse) { @Parameter( ref = "reverse" ) @QueryParam("reverse") Boolean reverse) {
@ -92,7 +96,7 @@ public class CrossChainResource {
List<CrossChainTradeData> crossChainTradesData = new ArrayList<>(); List<CrossChainTradeData> crossChainTradesData = new ArrayList<>();
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {
Map<ByteArray, Supplier<ACCT>> acctsByCodeHash = SupportedBlockchain.getAcctMap(); Map<ByteArray, Supplier<ACCT>> acctsByCodeHash = SupportedBlockchain.getFilteredAcctMap(foreignBlockchain);
for (Map.Entry<ByteArray, Supplier<ACCT>> acctInfo : acctsByCodeHash.entrySet()) { for (Map.Entry<ByteArray, Supplier<ACCT>> acctInfo : acctsByCodeHash.entrySet()) {
byte[] codeHash = acctInfo.getKey().value; byte[] codeHash = acctInfo.getKey().value;
@ -131,6 +135,11 @@ public class CrossChainResource {
) )
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE}) @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE})
public List<CrossChainTradeSummary> getCompletedTrades( public List<CrossChainTradeSummary> getCompletedTrades(
@Parameter(
description = "Limit to specific blockchain",
example = "LITECOIN",
schema = @Schema(implementation = SupportedBlockchain.class)
) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain,
@Parameter( @Parameter(
description = "Only return trades that completed on/after this timestamp (milliseconds since epoch)", description = "Only return trades that completed on/after this timestamp (milliseconds since epoch)",
example = "1597310000000" example = "1597310000000"
@ -165,7 +174,7 @@ public class CrossChainResource {
List<CrossChainTradeSummary> crossChainTrades = new ArrayList<>(); List<CrossChainTradeSummary> crossChainTrades = new ArrayList<>();
Map<ByteArray, Supplier<ACCT>> acctsByCodeHash = SupportedBlockchain.getAcctMap(); Map<ByteArray, Supplier<ACCT>> acctsByCodeHash = SupportedBlockchain.getFilteredAcctMap(foreignBlockchain);
for (Map.Entry<ByteArray, Supplier<ACCT>> acctInfo : acctsByCodeHash.entrySet()) { for (Map.Entry<ByteArray, Supplier<ACCT>> acctInfo : acctsByCodeHash.entrySet()) {
byte[] codeHash = acctInfo.getKey().value; byte[] codeHash = acctInfo.getKey().value;

View File

@ -1,6 +1,7 @@
package org.qortal.api.resource; package org.qortal.api.resource;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -9,12 +10,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -30,6 +33,7 @@ import org.qortal.asset.Asset;
import org.qortal.controller.tradebot.AcctTradeBot; import org.qortal.controller.tradebot.AcctTradeBot;
import org.qortal.controller.tradebot.TradeBot; import org.qortal.controller.tradebot.TradeBot;
import org.qortal.crosschain.ForeignBlockchain; import org.qortal.crosschain.ForeignBlockchain;
import org.qortal.crosschain.SupportedBlockchain;
import org.qortal.crosschain.ACCT; import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.AcctMode; import org.qortal.crosschain.AcctMode;
import org.qortal.crypto.Crypto; import org.qortal.crypto.Crypto;
@ -65,13 +69,20 @@ public class CrossChainTradeBotResource {
) )
@ApiErrors({ApiError.REPOSITORY_ISSUE}) @ApiErrors({ApiError.REPOSITORY_ISSUE})
public List<TradeBotData> getTradeBotStates( public List<TradeBotData> getTradeBotStates(
// TODO: optional filter for foreign blockchain(s)? @Parameter(
) { description = "Limit to specific blockchain",
example = "LITECOIN",
schema = @Schema(implementation = SupportedBlockchain.class)
) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain) {
Security.checkApiCallAllowed(request); Security.checkApiCallAllowed(request);
try (final Repository repository = RepositoryManager.getRepository()) { try (final Repository repository = RepositoryManager.getRepository()) {
// TODO: maybe sub-class returned trade-bot data according to which trade-bot/ACCT applies? List<TradeBotData> allTradeBotData = repository.getCrossChainRepository().getAllTradeBotData();
return repository.getCrossChainRepository().getAllTradeBotData();
if (foreignBlockchain == null)
return allTradeBotData;
return allTradeBotData.stream().filter(tradeBotData -> tradeBotData.getForeignBlockchain().equals(foreignBlockchain.name())).collect(Collectors.toList());
} catch (DataException e) { } catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
} }

View File

@ -1,6 +1,7 @@
package org.qortal.crosschain; package org.qortal.crosschain;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -50,6 +51,9 @@ public enum SupportedBlockchain {
.flatMap(List::stream) .flatMap(List::stream)
.collect(Collectors.toUnmodifiableMap(Triple::getA, Triple::getC)); .collect(Collectors.toUnmodifiableMap(Triple::getA, Triple::getC));
private static final Map<String, SupportedBlockchain> blockchainsByName = Arrays.stream(SupportedBlockchain.values())
.collect(Collectors.toUnmodifiableMap(Enum::name, blockchain -> blockchain));
private final List<Triple<String, byte[], Supplier<ACCT>>> supportedAccts; private final List<Triple<String, byte[], Supplier<ACCT>>> supportedAccts;
SupportedBlockchain(List<Triple<String, byte[], Supplier<ACCT>>> supportedAccts) { SupportedBlockchain(List<Triple<String, byte[], Supplier<ACCT>>> supportedAccts) {
@ -63,6 +67,25 @@ public enum SupportedBlockchain {
return supportedAcctsByCodeHash; return supportedAcctsByCodeHash;
} }
public static Map<ByteArray, Supplier<ACCT>> getFilteredAcctMap(SupportedBlockchain blockchain) {
if (blockchain == null)
return getAcctMap();
return blockchain.supportedAccts.stream()
.collect(Collectors.toUnmodifiableMap(triple -> new ByteArray(triple.getB()), Triple::getC));
}
public static Map<ByteArray, Supplier<ACCT>> getFilteredAcctMap(String specificBlockchain) {
if (specificBlockchain == null)
return getAcctMap();
SupportedBlockchain blockchain = blockchainsByName.get(specificBlockchain);
if (blockchain == null)
return Collections.emptyMap();
return getFilteredAcctMap(blockchain);
}
public static ACCT getAcctByCodeHash(byte[] codeHash) { public static ACCT getAcctByCodeHash(byte[] codeHash) {
ByteArray wrappedCodeHash = new ByteArray(codeHash); ByteArray wrappedCodeHash = new ByteArray(codeHash);

View File

@ -4,10 +4,13 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.qortal.api.ApiError; import org.qortal.api.ApiError;
import org.qortal.api.resource.CrossChainResource; import org.qortal.api.resource.CrossChainResource;
import org.qortal.crosschain.SupportedBlockchain;
import org.qortal.test.common.ApiCommon; import org.qortal.test.common.ApiCommon;
public class CrossChainApiTests extends ApiCommon { public class CrossChainApiTests extends ApiCommon {
private static final SupportedBlockchain SPECIFIC_BLOCKCHAIN = null;
private CrossChainResource crossChainResource; private CrossChainResource crossChainResource;
@Before @Before
@ -17,12 +20,13 @@ public class CrossChainApiTests extends ApiCommon {
@Test @Test
public void testGetTradeOffers() { public void testGetTradeOffers() {
assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getTradeOffers(limit, offset, reverse)); assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getTradeOffers(SPECIFIC_BLOCKCHAIN, limit, offset, reverse));
} }
@Test @Test
public void testGetCompletedTrades() { public void testGetCompletedTrades() {
assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(System.currentTimeMillis() /*minimumTimestamp*/, limit, offset, reverse)); long minimumTimestamp = System.currentTimeMillis();
assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, minimumTimestamp, limit, offset, reverse));
} }
@Test @Test
@ -31,8 +35,8 @@ public class CrossChainApiTests extends ApiCommon {
Integer offset = null; Integer offset = null;
Boolean reverse = null; Boolean reverse = null;
assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(-1L /*minimumTimestamp*/, limit, offset, reverse)); assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, -1L /*minimumTimestamp*/, limit, offset, reverse));
assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(0L /*minimumTimestamp*/, limit, offset, reverse)); assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, 0L /*minimumTimestamp*/, limit, offset, reverse));
} }
} }