mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 15:10:44 +01:00
Merge pull request #6088 from ghubstan/2-improve-grpc-exception-status-code-mapping
Send meaningful io.grpc.Status.Code to gRPC clients [No. 2]
This commit is contained in:
commit
20a3ec0592
24 changed files with 316 additions and 96 deletions
|
@ -26,6 +26,9 @@ import bisq.common.util.Utilities;
|
|||
|
||||
import bisq.proto.grpc.BalancesInfo;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
@ -190,6 +193,15 @@ public class MethodTest extends ApiTestCase {
|
|||
return Utilities.bytesAsHexString(s.getBytes(UTF_8));
|
||||
}
|
||||
|
||||
protected static Status.Code getStatusRuntimeExceptionStatusCode(Exception grpcException) {
|
||||
if (grpcException instanceof StatusRuntimeException)
|
||||
return ((StatusRuntimeException) grpcException).getStatus().getCode();
|
||||
else
|
||||
throw new IllegalArgumentException(
|
||||
format("Expected a io.grpc.StatusRuntimeException argument, but got a %s",
|
||||
grpcException.getClass().getName()));
|
||||
}
|
||||
|
||||
protected void verifyNoLoggedNodeExceptions() {
|
||||
var loggedExceptions = getNodeExceptionMessages();
|
||||
if (loggedExceptions != null) {
|
||||
|
|
|
@ -61,7 +61,7 @@ public class RegisterDisputeAgentsTest extends MethodTest {
|
|||
public void testRegisterArbitratorShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
arbClient.registerDisputeAgent(ARBITRATOR, DEV_PRIVILEGE_PRIV_KEY));
|
||||
assertEquals("INVALID_ARGUMENT: arbitrators must be registered in a Bisq UI",
|
||||
assertEquals("UNIMPLEMENTED: arbitrators must be registered in a Bisq UI",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.junit.jupiter.api.TestMethodOrder;
|
|||
|
||||
import static bisq.apitest.config.ApiTestConfig.BSQ;
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static io.grpc.Status.Code.NOT_FOUND;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
@ -140,6 +141,8 @@ public class BsqSwapOfferTest extends AbstractOfferTest {
|
|||
break;
|
||||
} catch (Exception ex) {
|
||||
log.warn(ex.getMessage());
|
||||
var statusCode = getStatusRuntimeExceptionStatusCode(ex);
|
||||
assertEquals(NOT_FOUND, statusCode, "Expected a NOT_FOUND status code from server");
|
||||
|
||||
if (numFetchAttempts >= 9)
|
||||
fail(format("Alice giving up on fetching her (my) bsq swap offer after %d attempts.", numFetchAttempts), ex);
|
||||
|
@ -160,6 +163,8 @@ public class BsqSwapOfferTest extends AbstractOfferTest {
|
|||
break;
|
||||
} catch (Exception ex) {
|
||||
log.warn(ex.getMessage());
|
||||
var statusCode = getStatusRuntimeExceptionStatusCode(ex);
|
||||
assertEquals(NOT_FOUND, statusCode, "Expected a NOT_FOUND status code from server");
|
||||
|
||||
if (numFetchAttempts > 9)
|
||||
fail(format("Bob gave up on fetching available bsq swap offer after %d attempts.", numFetchAttempts), ex);
|
||||
|
|
|
@ -437,7 +437,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
|||
NO_TRIGGER_PRICE,
|
||||
ACTIVATE_OFFER,
|
||||
MKT_PRICE_MARGIN_ONLY));
|
||||
String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: cannot set mkt price margin or"
|
||||
+ " trigger price on fixed price bsq offer with id '%s'",
|
||||
originalOffer.getId());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
@ -465,7 +465,7 @@ public class EditOfferTest extends AbstractOfferTest {
|
|||
newTriggerPriceAsLong,
|
||||
ACTIVATE_OFFER,
|
||||
TRIGGER_PRICE_ONLY));
|
||||
String expectedExceptionMessage = format("UNKNOWN: cannot set mkt price margin or"
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: cannot set mkt price margin or"
|
||||
+ " trigger price on fixed price bsq offer with id '%s'",
|
||||
originalOffer.getId());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
@ -850,8 +850,10 @@ public class EditOfferTest extends AbstractOfferTest {
|
|||
NO_TRIGGER_PRICE,
|
||||
ACTIVATE_OFFER,
|
||||
TRIGGER_PRICE_ONLY));
|
||||
String expectedExceptionMessage = format("UNKNOWN: cannot edit bsq swap offer with id '%s'",
|
||||
originalOffer.getId());
|
||||
String expectedExceptionMessage =
|
||||
format("INVALID_ARGUMENT: cannot edit bsq swap offer with id '%s',"
|
||||
+ " replace it with a new swap offer instead",
|
||||
originalOffer.getId());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
}
|
||||
|
||||
|
|
|
@ -207,10 +207,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
|||
String receiverAddress = contract.getIsBuyerMakerAndSellerTaker()
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
// TODO Fix trade vol src bug for subclasses.
|
||||
// This bug was fixed for production CLI with https://github.com/bisq-network/bisq/pull/5704 on Sep 27, 2021
|
||||
String sendBsqAmount = trade.getOffer().getVolume();
|
||||
// String sendBsqAmount = trade.getTradeVolume();
|
||||
String sendBsqAmount = trade.getTradeVolume();
|
||||
log.debug("Sending {} BSQ to address {}", sendBsqAmount, receiverAddress);
|
||||
grpcClient.sendBsq(receiverAddress, sendBsqAmount, "");
|
||||
}
|
||||
|
@ -219,10 +216,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
|||
GrpcClient grpcClient,
|
||||
TradeInfo trade) {
|
||||
var contract = trade.getContract();
|
||||
// TODO Fix trade vol src bug for subclasses.
|
||||
// This bug was fixed for production with https://github.com/bisq-network/bisq/pull/5704 on Sep 27, 2021
|
||||
var receiveAmountAsString = trade.getOffer().getVolume();
|
||||
// String receiveAmountAsString = trade.getTradeVolume();
|
||||
String receiveAmountAsString = trade.getTradeVolume();
|
||||
var address = contract.getIsBuyerMakerAndSellerTaker()
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
|
|
|
@ -36,6 +36,7 @@ import static bisq.apitest.config.ApiTestConfig.BSQ;
|
|||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.core.offer.OfferDirection.BUY;
|
||||
import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP;
|
||||
import static io.grpc.Status.Code.NOT_FOUND;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
@ -171,6 +172,9 @@ public class BsqSwapBuyBtcTradeTest extends AbstractTradeTest {
|
|||
return client.getTrade(tradeId);
|
||||
} catch (Exception ex) {
|
||||
log.warn(ex.getMessage());
|
||||
var statusCode = getStatusRuntimeExceptionStatusCode(ex);
|
||||
assertEquals(NOT_FOUND, statusCode, "Expected a NOT_FOUND status code from server");
|
||||
|
||||
if (numFetchAttempts > 9) {
|
||||
if (checkForLoggedExceptions) {
|
||||
printNodeExceptionMessages(log);
|
||||
|
|
|
@ -34,6 +34,7 @@ import static bisq.apitest.config.ApiTestConfig.BSQ;
|
|||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.core.offer.OfferDirection.SELL;
|
||||
import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP;
|
||||
import static io.grpc.Status.Code.NOT_FOUND;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
@ -169,6 +170,9 @@ public class BsqSwapSellBtcTradeTest extends AbstractTradeTest {
|
|||
return client.getTrade(tradeId);
|
||||
} catch (Exception ex) {
|
||||
log.warn(ex.getMessage());
|
||||
var statusCode = getStatusRuntimeExceptionStatusCode(ex);
|
||||
assertEquals(NOT_FOUND, statusCode, "Expected a NOT_FOUND status code from server");
|
||||
|
||||
if (numFetchAttempts > 9) {
|
||||
if (checkForLoggedExceptions) {
|
||||
printNodeExceptionMessages(log);
|
||||
|
|
|
@ -65,7 +65,7 @@ public class FailUnfailTradeTest extends AbstractTradeTest {
|
|||
aliceClient.failTrade(tradeId);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getTrade(tradeId));
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: trade with id '%s' not found", tradeId);
|
||||
String expectedExceptionMessage = format("NOT_FOUND: trade with id '%s' not found", tradeId);
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
||||
try {
|
||||
|
@ -86,7 +86,7 @@ public class FailUnfailTradeTest extends AbstractTradeTest {
|
|||
aliceClient.failTrade(tradeId);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getTrade(tradeId));
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: trade with id '%s' not found", tradeId);
|
||||
String expectedExceptionMessage = format("NOT_FOUND: trade with id '%s' not found", tradeId);
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
||||
try {
|
||||
|
@ -108,7 +108,7 @@ public class FailUnfailTradeTest extends AbstractTradeTest {
|
|||
aliceClient.failTrade(tradeId);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getTrade(tradeId));
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: trade with id '%s' not found", tradeId);
|
||||
String expectedExceptionMessage = format("NOT_FOUND: trade with id '%s' not found", tradeId);
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
||||
try {
|
||||
|
@ -130,7 +130,7 @@ public class FailUnfailTradeTest extends AbstractTradeTest {
|
|||
aliceClient.failTrade(tradeId);
|
||||
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getTrade(tradeId));
|
||||
String expectedExceptionMessage = format("INVALID_ARGUMENT: trade with id '%s' not found", tradeId);
|
||||
String expectedExceptionMessage = format("NOT_FOUND: trade with id '%s' not found", tradeId);
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
|
||||
try {
|
||||
|
|
|
@ -58,7 +58,7 @@ public class BtcTxFeeRateTest extends MethodTest {
|
|||
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.setTxFeeRate(1));
|
||||
String expectedExceptionMessage =
|
||||
format("UNKNOWN: tx fee rate preference must be >= %d sats/byte",
|
||||
format("INVALID_ARGUMENT: tx fee rate preference must be >= %d sats/byte",
|
||||
currentTxFeeRateInfo.getMinFeeServiceRate());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
@Order(2)
|
||||
public void testGetBalanceOnEncryptedWalletShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -58,7 +58,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.getBtcBalances(); // should not throw 'wallet locked' exception
|
||||
sleep(4500); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -67,7 +67,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.unlockWallet("first-password", 3);
|
||||
sleep(4000); // let unlock timeout expire
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -76,14 +76,14 @@ public class WalletProtectionTest extends MethodTest {
|
|||
aliceClient.unlockWallet("first-password", 60);
|
||||
aliceClient.lockWallet();
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.getBtcBalances());
|
||||
assertEquals("UNKNOWN: wallet is locked", exception.getMessage());
|
||||
assertEquals("FAILED_PRECONDITION: wallet is locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
public void testLockWalletWhenWalletAlreadyLockedShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.lockWallet());
|
||||
assertEquals("UNKNOWN: wallet is already locked", exception.getMessage());
|
||||
assertEquals("ALREADY_EXISTS: wallet is already locked", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -110,7 +110,7 @@ public class WalletProtectionTest extends MethodTest {
|
|||
public void testSetNewWalletPasswordWithIncorrectNewPasswordShouldThrowException() {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
aliceClient.setWalletPassword("bad old password", "irrelevant"));
|
||||
assertEquals("UNKNOWN: incorrect old password", exception.getMessage());
|
||||
assertEquals("INVALID_ARGUMENT: incorrect old password", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -740,7 +740,8 @@ public class CliMain {
|
|||
}
|
||||
}
|
||||
} catch (StatusRuntimeException ex) {
|
||||
// Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message
|
||||
// Remove the leading gRPC status code, e.g., INVALID_ARGUMENT,
|
||||
// NOT_FOUND, ..., UNKNOWN from the exception message.
|
||||
String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", "");
|
||||
if (message.equals("io exception"))
|
||||
throw new RuntimeException(message + ", server may not be running", ex);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.NotAvailableException;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||
|
@ -79,12 +80,12 @@ class CoreDisputeAgentsService {
|
|||
|
||||
void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
if (!p2PService.isBootstrapped())
|
||||
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||
throw new NotAvailableException("p2p service is not bootstrapped yet");
|
||||
|
||||
if (config.baseCurrencyNetwork.isMainnet()
|
||||
|| config.baseCurrencyNetwork.isDaoBetaNet()
|
||||
|| !config.useLocalhostForP2P)
|
||||
throw new IllegalStateException("dispute agents must be registered in a Bisq UI");
|
||||
throw new UnsupportedOperationException("dispute agents must be registered in a Bisq UI");
|
||||
|
||||
if (!registrationKey.equals(DEV_PRIVILEGE_PRIV_KEY))
|
||||
throw new IllegalArgumentException("invalid registration key");
|
||||
|
@ -95,7 +96,7 @@ class CoreDisputeAgentsService {
|
|||
String signature;
|
||||
switch (supportType.get()) {
|
||||
case ARBITRATION:
|
||||
throw new IllegalArgumentException("arbitrators must be registered in a Bisq UI");
|
||||
throw new UnsupportedOperationException("arbitrators must be registered in a Bisq UI");
|
||||
case MEDIATION:
|
||||
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
||||
signature = mediatorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||
|
@ -107,7 +108,7 @@ class CoreDisputeAgentsService {
|
|||
registerRefundAgent(nodeAddress, languageCodes, ecKey, signature);
|
||||
return;
|
||||
case TRADE:
|
||||
throw new IllegalArgumentException("trade agent registration not supported");
|
||||
throw new UnsupportedOperationException("trade agent registration not supported");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(format("unknown dispute agent type '%s'", disputeAgentType));
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.NotFoundException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
@ -45,7 +47,7 @@ class CoreHelpService {
|
|||
return readHelpFile(resourceFile);
|
||||
} catch (NullPointerException ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException(format("no help found for api method %s", methodName));
|
||||
throw new NotFoundException(format("no help found for api method %s", methodName));
|
||||
} catch (IOException ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException(format("could not read %s help doc", methodName));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.NotFoundException;
|
||||
import bisq.core.monetary.Altcoin;
|
||||
import bisq.core.monetary.Price;
|
||||
import bisq.core.offer.Offer;
|
||||
|
@ -145,7 +146,7 @@ class CoreOffersService {
|
|||
|
||||
Offer getOffer(String id) {
|
||||
return findAvailableOffer(id).orElseThrow(() ->
|
||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||
new NotFoundException(format("offer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
Optional<Offer> findAvailableOffer(String id) {
|
||||
|
@ -158,7 +159,7 @@ class CoreOffersService {
|
|||
|
||||
OpenOffer getMyOffer(String id) {
|
||||
return findMyOpenOffer(id).orElseThrow(() ->
|
||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||
new NotFoundException(format("offer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
Optional<OpenOffer> findMyOpenOffer(String id) {
|
||||
|
@ -170,7 +171,7 @@ class CoreOffersService {
|
|||
|
||||
Offer getBsqSwapOffer(String id) {
|
||||
return findAvailableBsqSwapOffer(id).orElseThrow(() ->
|
||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||
new NotFoundException(format("offer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
Optional<Offer> findAvailableBsqSwapOffer(String id) {
|
||||
|
@ -184,7 +185,7 @@ class CoreOffersService {
|
|||
|
||||
Offer getMyBsqSwapOffer(String id) {
|
||||
return findMyBsqSwapOffer(id).orElseThrow(() ->
|
||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||
new NotFoundException(format("offer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
Optional<Offer> findMyBsqSwapOffer(String id) {
|
||||
|
@ -272,14 +273,14 @@ class CoreOffersService {
|
|||
.filter(open -> open.getOffer().isMyOffer(keyRing))
|
||||
.filter(open -> open.getOffer().isBsqSwapOffer())
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException(format("openoffer with id '%s' not found", id)));
|
||||
new NotFoundException(format("openoffer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
OpenOffer getMyOpenOffer(String id) {
|
||||
return openOfferManager.getOpenOfferById(id)
|
||||
.filter(open -> open.getOffer().isMyOffer(keyRing))
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException(format("offer with id '%s' not found", id)));
|
||||
new NotFoundException(format("offer with id '%s' not found", id)));
|
||||
}
|
||||
|
||||
boolean isMyOffer(Offer offer) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.NotFoundException;
|
||||
import bisq.core.btc.model.AddressEntry;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.offer.Offer;
|
||||
|
@ -204,7 +205,7 @@ class CoreTradesService {
|
|||
|
||||
verifyTradeIsNotClosed(tradeId);
|
||||
var trade = getOpenTrade(tradeId).orElseThrow(() ->
|
||||
new IllegalArgumentException(format("trade with id '%s' not found", tradeId)));
|
||||
new NotFoundException(format("trade with id '%s' not found", tradeId)));
|
||||
log.info("Closing trade {}", tradeId);
|
||||
tradeManager.onTradeCompleted(trade);
|
||||
}
|
||||
|
@ -215,7 +216,7 @@ class CoreTradesService {
|
|||
|
||||
verifyTradeIsNotClosed(tradeId);
|
||||
var trade = getOpenTrade(tradeId).orElseThrow(() ->
|
||||
new IllegalArgumentException(format("trade with id '%s' not found", tradeId)));
|
||||
new NotFoundException(format("trade with id '%s' not found", tradeId)));
|
||||
|
||||
verifyIsValidBTCAddress(toAddress);
|
||||
|
||||
|
@ -263,7 +264,7 @@ class CoreTradesService {
|
|||
return closedTrade.get();
|
||||
|
||||
return tradeManager.findBsqSwapTradeById(tradeId).orElseThrow(() ->
|
||||
new IllegalArgumentException(format("trade with id '%s' not found", tradeId)));
|
||||
new NotFoundException(format("trade with id '%s' not found", tradeId)));
|
||||
}
|
||||
|
||||
String getTradeRole(TradeModel tradeModel) {
|
||||
|
@ -285,7 +286,7 @@ class CoreTradesService {
|
|||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||
return getOpenTrade(tradeId).orElseGet(() ->
|
||||
getClosedTrade(tradeId).orElseThrow(() ->
|
||||
new IllegalArgumentException(format("trade with id '%s' not found", tradeId))
|
||||
new NotFoundException(format("trade with id '%s' not found", tradeId))
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -333,7 +334,7 @@ class CoreTradesService {
|
|||
tradeManager.addFailedTradeToPendingTrades(failedTrade);
|
||||
log.info("Failed trade {} changed to open trade.", tradeId);
|
||||
}, () -> {
|
||||
throw new IllegalArgumentException(format("failed trade '%s' not found", tradeId));
|
||||
throw new NotFoundException(format("failed trade '%s' not found", tradeId));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
package bisq.core.api;
|
||||
|
||||
import bisq.core.api.exception.AlreadyExistsException;
|
||||
import bisq.core.api.exception.FailedPreconditionException;
|
||||
import bisq.core.api.exception.NotAvailableException;
|
||||
import bisq.core.api.exception.NotFoundException;
|
||||
import bisq.core.api.model.AddressBalanceInfo;
|
||||
import bisq.core.api.model.BalancesInfo;
|
||||
import bisq.core.api.model.BsqBalanceInfo;
|
||||
|
@ -152,7 +156,7 @@ class CoreWalletsService {
|
|||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
if (balances.getAvailableBalance().get() == null)
|
||||
throw new IllegalStateException("balance is not yet available");
|
||||
throw new NotAvailableException("balance is not yet available");
|
||||
|
||||
switch (currencyCode.trim().toUpperCase()) {
|
||||
case "BSQ":
|
||||
|
@ -241,13 +245,13 @@ class CoreWalletsService {
|
|||
txFeePerVbyte.value);
|
||||
bsqTransferService.sendFunds(model, callback);
|
||||
} catch (InsufficientMoneyException ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException("cannot send bsq due to insufficient funds", ex);
|
||||
log.error(ex.toString());
|
||||
throw new NotAvailableException("cannot send bsq due to insufficient funds", ex);
|
||||
} catch (NumberFormatException
|
||||
| BsqChangeBelowDustException
|
||||
| TransactionVerificationException
|
||||
| WalletException ex) {
|
||||
log.error("", ex);
|
||||
log.error(ex.toString());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
@ -299,11 +303,11 @@ class CoreWalletsService {
|
|||
memo.isEmpty() ? null : memo,
|
||||
callback);
|
||||
} catch (AddressEntryException ex) {
|
||||
log.error("", ex);
|
||||
log.error(ex.toString());
|
||||
throw new IllegalStateException("cannot send btc from any addresses in wallet", ex);
|
||||
} catch (InsufficientFundsException | InsufficientMoneyException ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException("cannot send btc due to insufficient funds", ex);
|
||||
log.error(ex.toString());
|
||||
throw new NotAvailableException("cannot send btc due to insufficient funds", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +366,7 @@ class CoreWalletsService {
|
|||
}, MoreExecutors.directExecutor());
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error("", ex);
|
||||
log.error(ex.toString());
|
||||
throw new IllegalStateException("could not request fees from fee service", ex);
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +375,7 @@ class CoreWalletsService {
|
|||
ResultHandler resultHandler) {
|
||||
long minFeePerVbyte = feeService.getMinFeePerVByte();
|
||||
if (txFeeRate < minFeePerVbyte)
|
||||
throw new IllegalStateException(
|
||||
throw new IllegalArgumentException(
|
||||
format("tx fee rate preference must be >= %d sats/byte", minFeePerVbyte));
|
||||
|
||||
preferences.setUseCustomWithdrawalTxFee(true);
|
||||
|
@ -416,11 +420,11 @@ class CoreWalletsService {
|
|||
if (newPassword != null && !newPassword.isEmpty()) {
|
||||
// TODO Validate new password before replacing old password.
|
||||
if (!walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||
throw new FailedPreconditionException("wallet is not encrypted with a password");
|
||||
|
||||
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
||||
if (!walletsManager.checkAESKey(aesKey))
|
||||
throw new IllegalStateException("incorrect old password");
|
||||
throw new IllegalArgumentException("incorrect old password");
|
||||
|
||||
walletsManager.decryptWallets(aesKey);
|
||||
aesKey = keyCrypterScrypt.deriveKey(newPassword);
|
||||
|
@ -430,7 +434,7 @@ class CoreWalletsService {
|
|||
}
|
||||
|
||||
if (walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is encrypted with a password");
|
||||
throw new AlreadyExistsException("wallet is already encrypted with a password");
|
||||
|
||||
// TODO Validate new password.
|
||||
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
||||
|
@ -440,10 +444,10 @@ class CoreWalletsService {
|
|||
|
||||
void lockWallet() {
|
||||
if (!walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||
throw new FailedPreconditionException("wallet is not encrypted with a password");
|
||||
|
||||
if (tempAesKey == null)
|
||||
throw new IllegalStateException("wallet is already locked");
|
||||
throw new AlreadyExistsException("wallet is already locked");
|
||||
|
||||
tempAesKey = null;
|
||||
}
|
||||
|
@ -457,7 +461,7 @@ class CoreWalletsService {
|
|||
tempAesKey = keyCrypterScrypt.deriveKey(password);
|
||||
|
||||
if (!walletsManager.checkAESKey(tempAesKey))
|
||||
throw new IllegalStateException("incorrect password");
|
||||
throw new IllegalArgumentException("incorrect password");
|
||||
|
||||
if (lockTimer != null) {
|
||||
// The user has called unlockwallet again, before the prior unlockwallet
|
||||
|
@ -488,7 +492,7 @@ class CoreWalletsService {
|
|||
|
||||
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
||||
if (!walletsManager.checkAESKey(aesKey))
|
||||
throw new IllegalStateException("incorrect password");
|
||||
throw new IllegalArgumentException("incorrect password");
|
||||
|
||||
walletsManager.decryptWallets(aesKey);
|
||||
walletsManager.backupWallets();
|
||||
|
@ -503,7 +507,7 @@ class CoreWalletsService {
|
|||
// to leave this check in place until certain AppStartupState will always work
|
||||
// as expected.
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
throw new NotAvailableException("wallet is not yet available");
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if wallets are not available or not encrypted.
|
||||
|
@ -511,28 +515,28 @@ class CoreWalletsService {
|
|||
verifyWalletAndNetworkIsReady();
|
||||
|
||||
if (!walletsManager.areWalletsAvailable())
|
||||
throw new IllegalStateException("wallet is not yet available");
|
||||
throw new NotAvailableException("wallet is not yet available");
|
||||
|
||||
if (!walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||
throw new FailedPreconditionException("wallet is not encrypted with a password");
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if wallets are encrypted and locked.
|
||||
void verifyEncryptedWalletIsUnlocked() {
|
||||
if (walletsManager.areWalletsEncrypted() && tempAesKey == null)
|
||||
throw new IllegalStateException("wallet is locked");
|
||||
throw new FailedPreconditionException("wallet is locked");
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if wallets and network are not ready.
|
||||
void verifyWalletAndNetworkIsReady() {
|
||||
if (!appStartupState.isWalletAndNetworkReady())
|
||||
throw new IllegalStateException("wallet and network is not yet initialized");
|
||||
throw new NotAvailableException("wallet and network are not yet initialized");
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if application is not fully initialized.
|
||||
void verifyApplicationIsFullyInitialized() {
|
||||
if (!appStartupState.isApplicationFullyInitialized())
|
||||
throw new IllegalStateException("server is not fully initialized");
|
||||
throw new NotAvailableException("server is not fully initialized");
|
||||
}
|
||||
|
||||
// Returns an Address for the string, or a RuntimeException if invalid.
|
||||
|
@ -541,7 +545,7 @@ class CoreWalletsService {
|
|||
return bsqFormatter.getAddressFromBsqAddress(address);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("", e);
|
||||
throw new IllegalStateException(format("%s is not a valid bsq address", address));
|
||||
throw new IllegalArgumentException(format("%s is not a valid bsq address", address));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,7 +556,7 @@ class CoreWalletsService {
|
|||
|
||||
if (!currencyCode.equalsIgnoreCase("BSQ")
|
||||
&& !currencyCode.equalsIgnoreCase("BTC"))
|
||||
throw new IllegalStateException(format("wallet does not support %s", currencyCode));
|
||||
throw new UnsupportedOperationException(format("wallet does not support %s", currencyCode));
|
||||
}
|
||||
|
||||
private void maybeSetWalletsManagerKey() {
|
||||
|
@ -593,15 +597,15 @@ class CoreWalletsService {
|
|||
|
||||
var availableBalance = balances.getAvailableBalance().get();
|
||||
if (availableBalance == null)
|
||||
throw new IllegalStateException("balance is not yet available");
|
||||
throw new NotAvailableException("balance is not yet available");
|
||||
|
||||
var reservedBalance = balances.getReservedBalance().get();
|
||||
if (reservedBalance == null)
|
||||
throw new IllegalStateException("reserved balance is not yet available");
|
||||
throw new NotAvailableException("reserved balance is not yet available");
|
||||
|
||||
var lockedBalance = balances.getLockedBalance().get();
|
||||
if (lockedBalance == null)
|
||||
throw new IllegalStateException("locked balance is not yet available");
|
||||
throw new NotAvailableException("locked balance is not yet available");
|
||||
|
||||
return new BtcBalanceInfo(availableBalance.value,
|
||||
reservedBalance.value,
|
||||
|
@ -613,7 +617,7 @@ class CoreWalletsService {
|
|||
private Coin getValidTransferAmount(String amount, CoinFormatter coinFormatter) {
|
||||
Coin amountAsCoin = parseToCoin(amount, coinFormatter);
|
||||
if (amountAsCoin.isLessThan(getMinNonDustOutput()))
|
||||
throw new IllegalStateException(format("%s is an invalid transfer amount", amount));
|
||||
throw new IllegalArgumentException(format("%s is an invalid transfer amount", amount));
|
||||
|
||||
return amountAsCoin;
|
||||
}
|
||||
|
@ -639,7 +643,7 @@ class CoreWalletsService {
|
|||
.findFirst();
|
||||
|
||||
if (!addressEntry.isPresent())
|
||||
throw new IllegalStateException(format("address %s not found in wallet", addressString));
|
||||
throw new NotFoundException(format("address %s not found in wallet", addressString));
|
||||
|
||||
return addressEntry.get();
|
||||
}
|
||||
|
@ -651,13 +655,13 @@ class CoreWalletsService {
|
|||
try {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx == null)
|
||||
throw new IllegalArgumentException(format("tx with id %s not found", txId));
|
||||
throw new NotFoundException(format("tx with id %s not found", txId));
|
||||
else
|
||||
return tx;
|
||||
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalArgumentException(
|
||||
log.error(ex.toString());
|
||||
throw new IllegalStateException(
|
||||
format("could not get transaction with id %s%ncause: %s",
|
||||
txId,
|
||||
ex.getMessage().toLowerCase()));
|
||||
|
|
|
@ -205,7 +205,8 @@ class EditOfferValidator {
|
|||
|
||||
private void checkNotBsqOffer() {
|
||||
if ("BSQ".equals(currentlyOpenOffer.getOffer().getCurrencyCode())) {
|
||||
throw new IllegalStateException(
|
||||
// An illegal argument is a user error.
|
||||
throw new IllegalArgumentException(
|
||||
format("cannot set mkt price margin or trigger price on fixed price bsq offer with id '%s'",
|
||||
currentlyOpenOffer.getId()));
|
||||
}
|
||||
|
@ -213,8 +214,10 @@ class EditOfferValidator {
|
|||
|
||||
private void checkNotBsqSwapOffer() {
|
||||
if (currentlyOpenOffer.getOffer().isBsqSwapOffer()) {
|
||||
throw new IllegalStateException(
|
||||
format("cannot edit bsq swap offer with id '%s'", currentlyOpenOffer.getId()));
|
||||
// An illegal argument is a user error.
|
||||
throw new IllegalArgumentException(
|
||||
format("cannot edit bsq swap offer with id '%s', replace it with a new swap offer instead",
|
||||
currentlyOpenOffer.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.api.exception;
|
||||
|
||||
import bisq.common.BisqException;
|
||||
|
||||
/**
|
||||
* To be thrown in cases when some value or state already exists, e.g., trying to lock
|
||||
* an encrypted wallet that is already locked.
|
||||
*/
|
||||
public class AlreadyExistsException extends BisqException {
|
||||
|
||||
public AlreadyExistsException(String format, Object... args) {
|
||||
super(format, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.api.exception;
|
||||
|
||||
import bisq.common.BisqException;
|
||||
|
||||
/**
|
||||
* To be thrown in cases when client is attempting to change some state requiring a
|
||||
* pre-conditional state to exist, e.g., when attempting to lock or unlock a wallet that
|
||||
* is not encrypted.
|
||||
*/
|
||||
public class FailedPreconditionException extends BisqException {
|
||||
|
||||
public FailedPreconditionException(String format, Object... args) {
|
||||
super(format, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.api.exception;
|
||||
|
||||
import bisq.common.BisqException;
|
||||
|
||||
/**
|
||||
* To be thrown in cases where some service or value, e.g.,
|
||||
* a wallet balance, or sufficient funds are unavailable.
|
||||
*/
|
||||
public class NotAvailableException extends BisqException {
|
||||
|
||||
public NotAvailableException(String format, Object... args) {
|
||||
super(format, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.api.exception;
|
||||
|
||||
import bisq.common.BisqException;
|
||||
|
||||
/**
|
||||
* To be thrown when a file or entity such as an Offer, PaymentAccount, or Trade
|
||||
* is not found by RPC methods such as GetOffer(id), PaymentAccount(id), or GetTrade(id).
|
||||
*
|
||||
* May also be used if a resource such as a File is not found.
|
||||
*/
|
||||
public class NotFoundException extends BisqException {
|
||||
|
||||
public NotFoundException(String format, Object... args) {
|
||||
super(format, args);
|
||||
}
|
||||
}
|
29
core/src/main/java/bisq/core/api/exception/package-info.java
Normal file
29
core/src/main/java/bisq/core/api/exception/package-info.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package contains exceptions thrown only from services in the bisq.core.api
|
||||
* package. They are needed by the gRPC daemon service classes in the
|
||||
* bisq.daemon.grpc package, for the purpose of mapping these custom exceptions to
|
||||
* meaningful io.grpc.Status.Code values sent to gRPC clients.
|
||||
*
|
||||
* If / when a Bisq webapp module is created on top of the core API, these exceptions
|
||||
* will serve the same purpose, to be mapped to meaningful HTTP status codes sent to
|
||||
* REST clients.
|
||||
*/
|
||||
|
||||
package bisq.core.api.exception;
|
|
@ -17,6 +17,11 @@
|
|||
|
||||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.exception.AlreadyExistsException;
|
||||
import bisq.core.api.exception.FailedPreconditionException;
|
||||
import bisq.core.api.exception.NotAvailableException;
|
||||
import bisq.core.api.exception.NotFoundException;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
@ -29,8 +34,7 @@ import java.util.function.Predicate;
|
|||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static io.grpc.Status.INVALID_ARGUMENT;
|
||||
import static io.grpc.Status.UNKNOWN;
|
||||
import static io.grpc.Status.*;
|
||||
|
||||
/**
|
||||
* The singleton instance of this class handles any expected core api Throwable by
|
||||
|
@ -40,9 +44,26 @@ import static io.grpc.Status.UNKNOWN;
|
|||
@Singleton
|
||||
class GrpcExceptionHandler {
|
||||
|
||||
private final Predicate<Throwable> isExpectedException = (t) ->
|
||||
t instanceof IllegalStateException || t instanceof IllegalArgumentException;
|
||||
private static final String CORE_API_EXCEPTION_PKG_NAME = NotFoundException.class.getPackage().getName();
|
||||
|
||||
/**
|
||||
* Returns true if Throwable is a custom core api exception instance,
|
||||
* or one of the following native Java exception instances:
|
||||
* <p>
|
||||
* <pre>
|
||||
* IllegalArgumentException
|
||||
* IllegalStateException
|
||||
* UnsupportedOperationException
|
||||
* </pre>
|
||||
* </p>
|
||||
*/
|
||||
private final Predicate<Throwable> isExpectedException = (t) ->
|
||||
t.getClass().getPackage().getName().equals(CORE_API_EXCEPTION_PKG_NAME)
|
||||
|| t instanceof IllegalArgumentException
|
||||
|| t instanceof IllegalStateException
|
||||
|| t instanceof UnsupportedOperationException;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Inject
|
||||
public GrpcExceptionHandler() {
|
||||
}
|
||||
|
@ -107,18 +128,27 @@ class GrpcExceptionHandler {
|
|||
};
|
||||
|
||||
private Status mapGrpcErrorStatus(Throwable t, String description) {
|
||||
// We default to the UNKNOWN status, except were the mapping of a core api
|
||||
// exception to a gRPC Status is obvious. If we ever use a gRPC reverse-proxy
|
||||
// to support RESTful clients, we will need to have more specific mappings
|
||||
// to support correct HTTP 1.1. status codes.
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
switch (t.getClass().getSimpleName()) {
|
||||
// We go ahead and use a switch statement instead of if, in anticipation
|
||||
// of more, specific exception mappings.
|
||||
case "IllegalArgumentException":
|
||||
return INVALID_ARGUMENT.withDescription(description);
|
||||
default:
|
||||
return UNKNOWN.withDescription(description);
|
||||
}
|
||||
// Check if a custom core.api.exception was thrown, so we can map it to a more
|
||||
// meaningful io.grpc.Status, something more useful to gRPC clients than UNKNOWN.
|
||||
if (t instanceof AlreadyExistsException)
|
||||
return ALREADY_EXISTS.withDescription(description);
|
||||
else if (t instanceof FailedPreconditionException)
|
||||
return FAILED_PRECONDITION.withDescription(description);
|
||||
else if (t instanceof NotFoundException)
|
||||
return NOT_FOUND.withDescription(description);
|
||||
else if (t instanceof NotAvailableException)
|
||||
return UNAVAILABLE.withDescription(description);
|
||||
|
||||
// If the above checks did not return an io.grpc.Status.Code, we map
|
||||
// the native Java exception to an io.grpc.Status.
|
||||
if (t instanceof IllegalArgumentException)
|
||||
return INVALID_ARGUMENT.withDescription(description);
|
||||
else if (t instanceof IllegalStateException)
|
||||
return UNKNOWN.withDescription(description);
|
||||
else if (t instanceof UnsupportedOperationException)
|
||||
return UNIMPLEMENTED.withDescription(description);
|
||||
else
|
||||
return UNKNOWN.withDescription(description);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -243,9 +243,9 @@ public class GrpcServiceRateMeteringConfig {
|
|||
timeUnit.toMillis(1) * numTimeUnits);
|
||||
rateMeterConfigs.stream().filter(c -> c.isConfigForGrpcService(grpcServiceClassName))
|
||||
.findFirst().ifPresentOrElse(
|
||||
(config) -> config.addMethodCallRateMeter(methodName, maxCalls, timeUnit, numTimeUnits),
|
||||
() -> rateMeterConfigs.add(new GrpcServiceRateMeteringConfig(grpcServiceClassName)
|
||||
.addMethodCallRateMeter(methodName, maxCalls, timeUnit, numTimeUnits)));
|
||||
(config) -> config.addMethodCallRateMeter(methodName, maxCalls, timeUnit, numTimeUnits),
|
||||
() -> rateMeterConfigs.add(new GrpcServiceRateMeteringConfig(grpcServiceClassName)
|
||||
.addMethodCallRateMeter(methodName, maxCalls, timeUnit, numTimeUnits)));
|
||||
}
|
||||
|
||||
public File build() {
|
||||
|
|
Loading…
Add table
Reference in a new issue