This is an automated email from the ASF dual-hosted git repository. myrle pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/fineract-cn-stellar-bridge.git
commit c1ead7b8e1dceae64e611724f34958d1610678a1 Author: Myrle Krantz <[email protected]> AuthorDate: Wed Jul 18 15:13:59 2018 +0200 Began work on mapping to Stellar currency issuer, in preparation for making outbound transactions. Had first go at inbound transactions. --- .../api/v1/client/StellarBridgeManager.java | 26 ++++++- .../api/v1/domain/BridgeConfiguration.java | 84 +++++++++++++++----- .../api/v1/domain/StellarCurrencyIssuer.java | 62 +++++++++++++++ .../api/v1/events/EventConstants.java | 2 + .../api/v1/domain/BridgeConfigurationTest.java | 8 +- .../cn/stellarbridge/TestBridgeConfiguration.java | 2 + .../internal/accounting/AccountingAdapter.java | 84 ++++++++++++++++++-- .../ChangeStellarCurrencyIssuerCommand.java | 31 ++++++++ .../internal/command/StellarPaymentCommand.java | 13 +++- .../handler/BridgeConfigurationCommandHandler.java | 19 +++++ .../handler/FineractPaymentCommandHandler.java | 5 +- .../handler/StellarPaymentCommandHandler.java | 18 +++-- .../HorizonServerEffectsListener.java | 8 +- .../HorizonServerPaymentObserver.java | 15 +++- .../internal/mapper/BridgeConfigurationMapper.java | 12 ++- .../mapper/StellarCurrencyIssuerMapper.java | 26 +++++++ .../repository/BridgeConfigurationEntity.java | 45 ++++++----- .../repository/StellarCurrencyIssuerEntity.java | 80 +++++++++++++++++++ .../StellarCurrencyIssuerRepository.java | 17 ++++ .../internal/repository/StellarCursorEntity.java | 2 +- .../service/StellarCurrencyIssuerService.java | 32 ++++++++ .../rest/BridgeConfigurationRestController.java | 6 +- .../rest/StellarCurrencyIssuerRestController.java | 91 ++++++++++++++++++++++ .../db/migrations/mariadb/V1__initial_setup.sql | 9 +++ 24 files changed, 618 insertions(+), 79 deletions(-) diff --git a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/client/StellarBridgeManager.java b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/client/StellarBridgeManager.java index 6128beb..04584c4 100644 --- a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/client/StellarBridgeManager.java +++ b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/client/StellarBridgeManager.java @@ -18,17 +18,19 @@ */ package org.apache.fineract.cn.stellarbridge.api.v1.client; +import java.util.List; import org.apache.fineract.cn.api.util.CustomFeignClientsConfiguration; import org.apache.fineract.cn.stellarbridge.api.v1.domain.BridgeConfiguration; +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @SuppressWarnings("unused") @FeignClient(value="stellarbridge-v1", path="/stellarbridge/v1", configuration = CustomFeignClientsConfiguration.class) public interface StellarBridgeManager { - @RequestMapping( value = "/config", method = RequestMethod.GET, @@ -37,6 +39,7 @@ public interface StellarBridgeManager { ) BridgeConfiguration getBridgeConfiguration(); + @RequestMapping( value = "/config", method = RequestMethod.PUT, @@ -44,4 +47,25 @@ public interface StellarBridgeManager { consumes = MediaType.APPLICATION_JSON_VALUE ) void setBridgeConfiguration(final BridgeConfiguration bridgeConfiguration); + +/** + * A currency with the same code can be available from many issuers. The stellar bridge needs to + * know which one to use. + */ + @RequestMapping( + value = "/currencyissuers", + method = RequestMethod.GET, + produces = {MediaType.ALL_VALUE}, + consumes = {MediaType.APPLICATION_JSON_VALUE} + ) + List<StellarCurrencyIssuer> getAllCurrencyIssuers(); + + @RequestMapping( + value = "/currencyissuers/{currencycode}/", + method = RequestMethod.GET, + produces = {MediaType.ALL_VALUE}, + consumes = {MediaType.APPLICATION_JSON_VALUE} + ) + StellarCurrencyIssuer getCurrencyIssuerForCurrency( + @PathVariable("currencycode") final String currencyCode); } diff --git a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfiguration.java b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfiguration.java index eb0c09a..9f65eac 100644 --- a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfiguration.java +++ b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfiguration.java @@ -24,38 +24,60 @@ import org.apache.fineract.cn.lang.validation.constraints.ValidIdentifier; @SuppressWarnings({"WeakerAccess", "unused"}) public class BridgeConfiguration { @ValidIdentifier - private String fineractIncomingAccountIdentifier; + private String fineractOutgoingStagingLedgerIdentifier; @ValidIdentifier - private String fineractOutgoingAccountIdentifier; + private String fineractStellarAssetsLedgerIdentifier; + + @ValidIdentifier + private String fineractIncomingStagingLedgerIdentifier; private String stellarAccountIdentifier; + private String stellarPrivateKey; + public BridgeConfiguration() { super(); } - public BridgeConfiguration(String fineractIncomingAccountIdentifier, - String fineractOutgoingAccountIdentifier, String stellarAccountIdentifier) { - this.fineractIncomingAccountIdentifier = fineractIncomingAccountIdentifier; - this.fineractOutgoingAccountIdentifier = fineractOutgoingAccountIdentifier; + public BridgeConfiguration( + String fineractOutgoingStagingLedgerIdentifier, + String fineractStellarAssetsLedgerIdentifier, + String fineractIncomingStagingLedgerIdentifier, + String stellarAccountIdentifier, + String stellarPrivateKey) { + this.fineractOutgoingStagingLedgerIdentifier = fineractOutgoingStagingLedgerIdentifier; + this.fineractStellarAssetsLedgerIdentifier = fineractStellarAssetsLedgerIdentifier; + this.fineractIncomingStagingLedgerIdentifier = fineractIncomingStagingLedgerIdentifier; this.stellarAccountIdentifier = stellarAccountIdentifier; + this.stellarPrivateKey = stellarPrivateKey; + } + + public String getFineractOutgoingStagingLedgerIdentifier() { + return fineractOutgoingStagingLedgerIdentifier; + } + + public void setFineractOutgoingStagingLedgerIdentifier( + String fineractOutgoingStagingLedgerIdentifier) { + this.fineractOutgoingStagingLedgerIdentifier = fineractOutgoingStagingLedgerIdentifier; } - public String getFineractIncomingAccountIdentifier() { - return fineractIncomingAccountIdentifier; + public String getFineractStellarAssetsLedgerIdentifier() { + return fineractStellarAssetsLedgerIdentifier; } - public void setFineractIncomingAccountIdentifier(String fineractIncomingAccountIdentifier) { - this.fineractIncomingAccountIdentifier = fineractIncomingAccountIdentifier; + public void setFineractStellarAssetsLedgerIdentifier( + String fineractStellarAssetsLedgerIdentifier) { + this.fineractStellarAssetsLedgerIdentifier = fineractStellarAssetsLedgerIdentifier; } - public String getFineractOutgoingAccountIdentifier() { - return fineractOutgoingAccountIdentifier; + public String getFineractIncomingStagingLedgerIdentifier() { + return fineractIncomingStagingLedgerIdentifier; } - public void setFineractOutgoingAccountIdentifier(String fineractOutgoingAccountIdentifier) { - this.fineractOutgoingAccountIdentifier = fineractOutgoingAccountIdentifier; + public void setFineractIncomingStagingLedgerIdentifier( + String fineractIncomingStagingLedgerIdentifier) { + this.fineractIncomingStagingLedgerIdentifier = fineractIncomingStagingLedgerIdentifier; } public String getStellarAccountIdentifier() { @@ -66,6 +88,14 @@ public class BridgeConfiguration { this.stellarAccountIdentifier = stellarAccountIdentifier; } + public String getStellarPrivateKey() { + return stellarPrivateKey; + } + + public void setStellarPrivateKey(String stellarPrivateKey) { + this.stellarPrivateKey = stellarPrivateKey; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -76,24 +106,36 @@ public class BridgeConfiguration { } BridgeConfiguration that = (BridgeConfiguration) o; return Objects - .equals(fineractIncomingAccountIdentifier, that.fineractIncomingAccountIdentifier) && + .equals(fineractOutgoingStagingLedgerIdentifier, + that.fineractOutgoingStagingLedgerIdentifier) + && Objects - .equals(fineractOutgoingAccountIdentifier, that.fineractOutgoingAccountIdentifier) && - Objects.equals(stellarAccountIdentifier, that.stellarAccountIdentifier); + .equals(fineractStellarAssetsLedgerIdentifier, + that.fineractStellarAssetsLedgerIdentifier) + && + Objects.equals(fineractIncomingStagingLedgerIdentifier, + that.fineractIncomingStagingLedgerIdentifier) && + Objects.equals(stellarAccountIdentifier, that.stellarAccountIdentifier) && + Objects.equals(stellarPrivateKey, that.stellarPrivateKey); } @Override public int hashCode() { - return Objects.hash(fineractIncomingAccountIdentifier, fineractOutgoingAccountIdentifier, - stellarAccountIdentifier); + return Objects + .hash(fineractOutgoingStagingLedgerIdentifier, fineractStellarAssetsLedgerIdentifier, + fineractIncomingStagingLedgerIdentifier, stellarAccountIdentifier, stellarPrivateKey); } @Override public String toString() { return "BridgeConfiguration{" + - "fineractIncomingAccountIdentifier='" + fineractIncomingAccountIdentifier + '\'' + - ", fineractOutgoingAccountIdentifier='" + fineractOutgoingAccountIdentifier + '\'' + + "fineractOutgoingStagingLedgerIdentifier='" + fineractOutgoingStagingLedgerIdentifier + '\'' + + + ", fineractStellarAssetsLedgerIdentifier='" + fineractStellarAssetsLedgerIdentifier + '\'' + + ", fineractIncomingStagingLedgerIdentifier='" + fineractIncomingStagingLedgerIdentifier + + '\'' + + ", stellarAccountIdentifier='" + stellarAccountIdentifier + '\'' + '}'; } diff --git a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/StellarCurrencyIssuer.java b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/StellarCurrencyIssuer.java new file mode 100644 index 0000000..b9097f6 --- /dev/null +++ b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/StellarCurrencyIssuer.java @@ -0,0 +1,62 @@ +package org.apache.fineract.cn.stellarbridge.api.v1.domain; + +import java.util.Objects; +import org.apache.fineract.cn.lang.validation.constraints.ValidIdentifier; + +public class StellarCurrencyIssuer { + @ValidIdentifier + String currencyCode; + + String stellarIssuer; + + public StellarCurrencyIssuer() { + } + + public StellarCurrencyIssuer(String currencyCode, String stellarIssuer) { + this.currencyCode = currencyCode; + this.stellarIssuer = stellarIssuer; + } + + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + public String getStellarIssuer() { + return stellarIssuer; + } + + public void setStellarIssuer(String stellarIssuer) { + this.stellarIssuer = stellarIssuer; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StellarCurrencyIssuer that = (StellarCurrencyIssuer) o; + return Objects.equals(currencyCode, that.currencyCode) && + Objects.equals(stellarIssuer, that.stellarIssuer); + } + + @Override + public int hashCode() { + + return Objects.hash(currencyCode, stellarIssuer); + } + + @Override + public String toString() { + return "StellarCurrencyIssuer{" + + "currencyCode='" + currencyCode + '\'' + + ", stellarIssuer='" + stellarIssuer + '\'' + + '}'; + } +} diff --git a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/events/EventConstants.java b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/events/EventConstants.java index c644272..8a4e5f8 100644 --- a/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/events/EventConstants.java +++ b/api/src/main/java/org/apache/fineract/cn/stellarbridge/api/v1/events/EventConstants.java @@ -25,10 +25,12 @@ public interface EventConstants { String SELECTOR_NAME = "action"; String INITIALIZE = "initialize"; String PUT_CONFIG = "put-config"; + String PUT_STELLAR_CURRENCY_ISSUER = "put-stellar-currency-issuer"; String STELLAR_PAYMENT_PROCESSED = "bridge-stellar-payment"; String FINERACT_PAYMENT_PROCESSED = "bridge-fineract-payment"; String SELECTOR_INITIALIZE = SELECTOR_NAME + " = '" + INITIALIZE + "'"; String SELECTOR_PUT_CONFIG = SELECTOR_NAME + " = '" + PUT_CONFIG + "'"; String SELECTOR_STELLAR_PAYMENT_PROCESSED = SELECTOR_NAME + " = '" + STELLAR_PAYMENT_PROCESSED + "'"; String SELECTOR_FINERACT_PAYMENT_PROCESSED = SELECTOR_NAME + " = '" + FINERACT_PAYMENT_PROCESSED + "'"; + String SELECTOR_PUT_STELLAR_CURRENCY_ISSUER = SELECTOR_NAME + " = '" + PUT_STELLAR_CURRENCY_ISSUER + "'"; } diff --git a/api/src/test/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfigurationTest.java b/api/src/test/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfigurationTest.java index cb13f9e..120d5c5 100644 --- a/api/src/test/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfigurationTest.java +++ b/api/src/test/java/org/apache/fineract/cn/stellarbridge/api/v1/domain/BridgeConfigurationTest.java @@ -33,7 +33,7 @@ public class BridgeConfigurationTest extends ValidationTest<BridgeConfiguration> @Override protected BridgeConfiguration createValidTestSubject() { - return new BridgeConfiguration("xxxx", "yyy", "zzz"); + return new BridgeConfiguration("aaa", "xxxx", "yyy", "zzz", "oo"); } @Parameterized.Parameters @@ -43,13 +43,13 @@ public class BridgeConfigurationTest extends ValidationTest<BridgeConfiguration> .adjustment(x -> {}) .valid(true)); ret.add(new ValidationTestCase<BridgeConfiguration>("nullIdentifier") - .adjustment(x -> x.setFineractIncomingAccountIdentifier(null)) + .adjustment(x -> x.setFineractIncomingStagingLedgerIdentifier(null)) .valid(false)); ret.add(new ValidationTestCase<BridgeConfiguration>("tooShortIdentifier") - .adjustment(x -> x.setFineractIncomingAccountIdentifier("z")) + .adjustment(x -> x.setFineractIncomingStagingLedgerIdentifier("z")) .valid(false)); ret.add(new ValidationTestCase<BridgeConfiguration>("tooLongPayload") - .adjustment(x -> x.setFineractIncomingAccountIdentifier(RandomStringUtils.randomAlphanumeric(513))) + .adjustment(x -> x.setFineractIncomingStagingLedgerIdentifier(RandomStringUtils.randomAlphanumeric(513))) .valid(false)); return ret; } diff --git a/component-test/src/main/java/org/apache/fineract/cn/stellarbridge/TestBridgeConfiguration.java b/component-test/src/main/java/org/apache/fineract/cn/stellarbridge/TestBridgeConfiguration.java index 1a91fde..c2af92d 100644 --- a/component-test/src/main/java/org/apache/fineract/cn/stellarbridge/TestBridgeConfiguration.java +++ b/component-test/src/main/java/org/apache/fineract/cn/stellarbridge/TestBridgeConfiguration.java @@ -122,6 +122,8 @@ public class TestBridgeConfiguration extends SuiteTestEnvironment { new BridgeConfiguration( "blah", "blah", + "blah", + "blah", "blah"); this.testSubject.setBridgeConfiguration(bridgeConfiguration); diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/accounting/AccountingAdapter.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/accounting/AccountingAdapter.java index 6075143..8513b34 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/accounting/AccountingAdapter.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/accounting/AccountingAdapter.java @@ -1,6 +1,15 @@ package org.apache.fineract.cn.stellarbridge.service.internal.accounting; import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Collections; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.fineract.cn.accounting.api.v1.client.JournalEntryAlreadyExistsException; +import org.apache.fineract.cn.accounting.api.v1.domain.Creditor; +import org.apache.fineract.cn.accounting.api.v1.domain.Debtor; +import org.apache.fineract.cn.accounting.api.v1.domain.JournalEntry; +import org.apache.fineract.cn.lang.DateConverter; +import org.apache.fineract.cn.stellarbridge.service.internal.config.StellarBridgeProperties; import org.apache.fineract.cn.stellarbridge.service.internal.repository.BridgeConfigurationEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -8,24 +17,83 @@ import org.springframework.stereotype.Service; @Service public class AccountingAdapter { private final JournalEntryCreator journalEntryCreator; + private final StellarBridgeProperties stellarBridgeProperties; @Autowired public AccountingAdapter( - final JournalEntryCreator journalEntryCreator) { + final JournalEntryCreator journalEntryCreator, + StellarBridgeProperties stellarBridgeProperties) { this.journalEntryCreator = journalEntryCreator; + this.stellarBridgeProperties = stellarBridgeProperties; } - public String adjustFineractBalances( + public String acceptIncomingPayment( final BridgeConfigurationEntity bridgeConfigurationEntity, final BigDecimal amount, - final String assetCode) { - //journalEntryCreator.createJournalEntry(journalEntry); - return null; + final String assetCode, + final LocalDateTime transactionDate) throws InterruptedException { + final JournalEntry journalEntryFundsAcceptance = new JournalEntry(); + final Creditor creditor = new Creditor( + bridgeConfigurationEntity.getFineractStellerLedger() + "." + assetCode, + amount.toString()); + final Debtor debtor = new Debtor( + bridgeConfigurationEntity.getFineractIncomingLedger() + "." + assetCode, + amount.toString()); + + journalEntryFundsAcceptance.setClerk(stellarBridgeProperties.getUser()); + journalEntryFundsAcceptance.setCreditors(Collections.singleton(creditor)); + journalEntryFundsAcceptance.setDebtors(Collections.singleton(debtor)); + journalEntryFundsAcceptance.setTransactionDate(DateConverter.toIsoString(transactionDate)); + journalEntryFundsAcceptance.setTransactionType("BCHQ"); + + final String transactionId = createWithUniqueTransactionIdentifier(journalEntryFundsAcceptance); + + Thread.sleep(2000); //TODO: replace with a wait on the journal entry creation. + + final JournalEntry journalEntryFundsForwarding = new JournalEntry(); + final Creditor fundsForwardingCreditor = new Creditor( + bridgeConfigurationEntity.getFineractStellerLedger() + "." + assetCode, + amount.toString()); + final Debtor fundsForwardingDebtor = new Debtor( + bridgeConfigurationEntity.getFineractIncomingLedger() + "." + assetCode, + amount.toString()); + + journalEntryFundsForwarding.setClerk(stellarBridgeProperties.getUser()); + journalEntryFundsForwarding.setCreditors(Collections.singleton(fundsForwardingCreditor)); + journalEntryFundsForwarding.setDebtors(Collections.singleton(fundsForwardingDebtor)); + journalEntryFundsForwarding.setTransactionDate(DateConverter.toIsoString(transactionDate)); + journalEntryFundsForwarding.setTransactionType("ICCT"); + + createWithUniqueTransactionIdentifier(journalEntryFundsForwarding); + + + return transactionId; } - public void tellFineractPaymentSucceeded(String fineractStagingAccountIdentifier, - String assetCode, BigDecimal amount) - { + private String createWithUniqueTransactionIdentifier(final JournalEntry journalEntry) { + while (true) { + try { + final String transactionUniqueifier = RandomStringUtils.random(26, true, true); + journalEntry.setTransactionIdentifier(formulateTransactionIdentifier(transactionUniqueifier)); + journalEntryCreator.createJournalEntry(journalEntry); + return transactionUniqueifier; + } catch (final JournalEntryAlreadyExistsException ignore) { + //Try again with a new uniqueifier. + } + } + } + private static String formulateTransactionIdentifier( + final String transactionUniqueifier) { + return "stellarbridge." + transactionUniqueifier; + } + + public void tellFineractPaymentSucceeded( + final String fineractOutgoingLedger, + final String fineractStellerLedger, + final String assetCode, + final BigDecimal amount) + { +//TODO: } } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/ChangeStellarCurrencyIssuerCommand.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/ChangeStellarCurrencyIssuerCommand.java new file mode 100644 index 0000000..34b9c9f --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/ChangeStellarCurrencyIssuerCommand.java @@ -0,0 +1,31 @@ +package org.apache.fineract.cn.stellarbridge.service.internal.command; + +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; + +public class ChangeStellarCurrencyIssuerCommand { + + private final String tenantIdentifier; + private final StellarCurrencyIssuer instance; + + public ChangeStellarCurrencyIssuerCommand(String tenantIdentifier, + StellarCurrencyIssuer instance) { + this.tenantIdentifier = tenantIdentifier; + this.instance = instance; + } + + public String getTenantIdentifier() { + return tenantIdentifier; + } + + public StellarCurrencyIssuer getInstance() { + return instance; + } + + @Override + public String toString() { + return "ChangeStellarCurrencyIssuerCommand{" + + "tenantIdentifier='" + tenantIdentifier + '\'' + + ", instance=" + instance + + '}'; + } +} diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/StellarPaymentCommand.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/StellarPaymentCommand.java index d1cd85a..d6be0d2 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/StellarPaymentCommand.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/StellarPaymentCommand.java @@ -1,17 +1,24 @@ package org.apache.fineract.cn.stellarbridge.service.internal.command; import java.math.BigDecimal; +import java.time.LocalDateTime; public class StellarPaymentCommand { private final String tenantIdentifier; private final String assetCode; private final BigDecimal amount; + private final LocalDateTime transactionDate; - public StellarPaymentCommand(String tenantIdentifier, String assetCode, BigDecimal amount) { + public StellarPaymentCommand( + String tenantIdentifier, + String assetCode, + BigDecimal amount, + LocalDateTime transactionDate) { this.tenantIdentifier = tenantIdentifier; this.assetCode = assetCode; this.amount = amount; + this.transactionDate = transactionDate; } public String getTenantIdentifier() { @@ -25,4 +32,8 @@ public class StellarPaymentCommand { public BigDecimal getAmount() { return amount; } + + public LocalDateTime getTransactionDate() { + return transactionDate; + } } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/BridgeConfigurationCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/BridgeConfigurationCommandHandler.java index 973eeb4..6019784 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/BridgeConfigurationCommandHandler.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/BridgeConfigurationCommandHandler.java @@ -23,9 +23,13 @@ import org.apache.fineract.cn.command.annotation.CommandHandler; import org.apache.fineract.cn.command.annotation.CommandLogLevel; import org.apache.fineract.cn.stellarbridge.api.v1.events.EventConstants; import org.apache.fineract.cn.stellarbridge.service.internal.command.ChangeConfigurationCommand; +import org.apache.fineract.cn.stellarbridge.service.internal.command.ChangeStellarCurrencyIssuerCommand; import org.apache.fineract.cn.stellarbridge.service.internal.mapper.BridgeConfigurationMapper; +import org.apache.fineract.cn.stellarbridge.service.internal.mapper.StellarCurrencyIssuerMapper; import org.apache.fineract.cn.stellarbridge.service.internal.repository.BridgeConfigurationEntity; import org.apache.fineract.cn.stellarbridge.service.internal.repository.BridgeConfigurationRepository; +import org.apache.fineract.cn.stellarbridge.service.internal.repository.StellarCurrencyIssuerEntity; +import org.apache.fineract.cn.stellarbridge.service.internal.repository.StellarCurrencyIssuerRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -34,13 +38,16 @@ import org.springframework.transaction.annotation.Transactional; public class BridgeConfigurationCommandHandler { private final BridgeConfigurationRepository bridgeConfigurationRepository; + private final StellarCurrencyIssuerRepository stellarCurrencyIssuerRepository; private final EventHelper eventHelper; @Autowired public BridgeConfigurationCommandHandler( final BridgeConfigurationRepository bridgeConfigurationRepository, + StellarCurrencyIssuerRepository stellarCurrencyIssuerRepository, final EventHelper eventHelper) { this.bridgeConfigurationRepository = bridgeConfigurationRepository; + this.stellarCurrencyIssuerRepository = stellarCurrencyIssuerRepository; this.eventHelper = eventHelper; } @@ -55,4 +62,16 @@ public class BridgeConfigurationCommandHandler { eventHelper.sendEvent(EventConstants.PUT_CONFIG, changeConfigurationCommand.tenantIdentifier(), null); } + + @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO) + @Transactional + public void handle(final ChangeStellarCurrencyIssuerCommand changeStellarCurrencyIssuerCommand) { + + final StellarCurrencyIssuerEntity entity = StellarCurrencyIssuerMapper.map( + changeStellarCurrencyIssuerCommand.getTenantIdentifier(), + changeStellarCurrencyIssuerCommand.getInstance()); + this.stellarCurrencyIssuerRepository.save(entity); + + eventHelper.sendEvent(EventConstants.PUT_STELLAR_CURRENCY_ISSUER, changeStellarCurrencyIssuerCommand.getTenantIdentifier(), null); + } } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/FineractPaymentCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/FineractPaymentCommandHandler.java index 328a542..76ef0ad 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/FineractPaymentCommandHandler.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/FineractPaymentCommandHandler.java @@ -57,7 +57,7 @@ public class FineractPaymentCommandHandler { StellarAddress.forTenant(command.getTargetAccount(), command.getSinkDomain())); final char[] decodedStellarPrivateKey = - bridgeConfigurationEntity.getStellarAccountPrivateKey(); + bridgeConfigurationEntity.getStellarAccountPrivateKey().toCharArray(); horizonServerUtilities.findPathPay( targetAccountId, @@ -65,7 +65,8 @@ public class FineractPaymentCommandHandler { decodedStellarPrivateKey); accountingAdapter.tellFineractPaymentSucceeded( - bridgeConfigurationEntity.getFineractStagingAccountIdentifier(), + bridgeConfigurationEntity.getFineractOutgoingLedger(), + bridgeConfigurationEntity.getFineractStellerLedger(), command.getAssetCode(), command.getAmount()); diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/StellarPaymentCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/StellarPaymentCommandHandler.java index 00ea780..c0c9904 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/StellarPaymentCommandHandler.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/command/handler/StellarPaymentCommandHandler.java @@ -31,16 +31,22 @@ public class StellarPaymentCommandHandler { @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO) @Transactional - public void handle(final StellarPaymentCommand command) { + public void handle(final StellarPaymentCommand command) throws InterruptedException { final Optional<BridgeConfigurationEntity> accountBridge = bridgeConfigurationRepository.findByTenantIdentifier(command.getTenantIdentifier()); - final Optional<String> transactionIdentifier = accountBridge.map(x -> accountingAdapter.adjustFineractBalances( - x, command.getAmount(), command.getAssetCode())); - - transactionIdentifier.ifPresent(x -> - eventHelper.sendEvent(EventConstants.STELLAR_PAYMENT_PROCESSED, command.getTenantIdentifier(), x)); + if (accountBridge.isPresent()) + { + final String transactionIdentifier = accountingAdapter.acceptIncomingPayment( + accountBridge.get(), command.getAmount(), command.getAssetCode(), + command.getTransactionDate()); + + eventHelper.sendEvent( + EventConstants.STELLAR_PAYMENT_PROCESSED, + command.getTenantIdentifier(), + transactionIdentifier); + } } } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerEffectsListener.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerEffectsListener.java index f9726a9..f071b8b 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerEffectsListener.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerEffectsListener.java @@ -1,6 +1,8 @@ package org.apache.fineract.cn.stellarbridge.service.internal.horizonadapter; import java.math.BigDecimal; +import java.time.Clock; +import java.time.LocalDateTime; import java.util.Date; import java.util.Optional; import org.apache.fineract.cn.command.gateway.CommandGateway; @@ -96,7 +98,8 @@ public class HorizonServerEffectsListener implements EventListener<EffectRespons return; final StellarPaymentCommand receivePaymentCommand = - new StellarPaymentCommand(toAccount.getTenantIdentifier(), assetCode, amount); + new StellarPaymentCommand(toAccount.getTenantIdentifier(), assetCode, amount, + LocalDateTime.now(Clock.systemUTC())); commandGateway.process(receivePaymentCommand); } else if (effect instanceof AccountDebitedEffectResponse) @@ -121,7 +124,8 @@ public class HorizonServerEffectsListener implements EventListener<EffectRespons return; final StellarPaymentCommand receivePaymentCommand = - new StellarPaymentCommand(toAccount.getTenantIdentifier(), assetCode, amount.negate()); + new StellarPaymentCommand(toAccount.getTenantIdentifier(), assetCode, amount.negate(), + LocalDateTime.now(Clock.systemUTC())); commandGateway.process(receivePaymentCommand); } else diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerPaymentObserver.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerPaymentObserver.java index 53e9acd..d1d0d96 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerPaymentObserver.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/horizonadapter/HorizonServerPaymentObserver.java @@ -13,6 +13,7 @@ import org.apache.fineract.cn.stellarbridge.service.internal.repository.StellarC import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.stereotype.Component; import org.stellar.sdk.KeyPair; import org.stellar.sdk.requests.EffectsRequestBuilder; @@ -29,11 +30,17 @@ public class HorizonServerPaymentObserver { @PostConstruct void init() { - final Optional<String> cursor = getCurrentCursor(); + try { + final Optional<String> cursor = this.getCurrentCursor(); - bridgeConfigurationRepository.findAll() - .forEach(config -> setupListeningForAccount( - StellarAccountId.mainAccount(config.getStellarAccountIdentifier()), cursor)); + bridgeConfigurationRepository.findAll() + .forEach(config -> setupListeningForAccount( + StellarAccountId.mainAccount(config.getStellarAccountIdentifier()), cursor)); + } + catch (InvalidDataAccessResourceUsageException x) { + //Nothing. If the repository hasn't been provisioned yet, there are no mapped accounts to + //listen in on. + } } @Autowired diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/BridgeConfigurationMapper.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/BridgeConfigurationMapper.java index 5605187..39b4128 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/BridgeConfigurationMapper.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/BridgeConfigurationMapper.java @@ -30,18 +30,22 @@ public class BridgeConfigurationMapper { public static BridgeConfiguration map(final BridgeConfigurationEntity toMap) { final BridgeConfiguration bridgeConfiguration = new BridgeConfiguration(); - bridgeConfiguration.setFineractIncomingAccountIdentifier(toMap.getFineractIncomingAccountIdentifier()); - bridgeConfiguration.setFineractOutgoingAccountIdentifier(toMap.getFineractOutgoingAccountIdentifier()); + bridgeConfiguration.setFineractIncomingStagingLedgerIdentifier(toMap.getFineractIncomingLedger()); + bridgeConfiguration.setFineractOutgoingStagingLedgerIdentifier(toMap.getFineractOutgoingLedger()); + bridgeConfiguration.setFineractStellarAssetsLedgerIdentifier(toMap.getFineractStellerLedger()); bridgeConfiguration.setStellarAccountIdentifier(toMap.getStellarAccountIdentifier()); + bridgeConfiguration.setStellarPrivateKey(toMap.getStellarAccountPrivateKey()); return bridgeConfiguration; } public static BridgeConfigurationEntity map(final String forTenant, final BridgeConfiguration toMap) { final BridgeConfigurationEntity bridgeConfigurationEntity = new BridgeConfigurationEntity(); bridgeConfigurationEntity.setTenantIdentifier(forTenant); - bridgeConfigurationEntity.setFineractIncomingAccountIdentifier(toMap.getFineractIncomingAccountIdentifier()); - bridgeConfigurationEntity.setFineractOutgoingAccountIdentifier(toMap.getFineractOutgoingAccountIdentifier()); + bridgeConfigurationEntity.setFineractIncomingLedger(toMap.getFineractIncomingStagingLedgerIdentifier()); + bridgeConfigurationEntity.setFineractOutgoingLedger(toMap.getFineractOutgoingStagingLedgerIdentifier()); + bridgeConfigurationEntity.setFineractStellerLedger(toMap.getFineractStellarAssetsLedgerIdentifier()); bridgeConfigurationEntity.setStellarAccountIdentifier(toMap.getStellarAccountIdentifier()); + bridgeConfigurationEntity.setStellarAccountPrivateKey(toMap.getStellarPrivateKey()); return bridgeConfigurationEntity; } } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/StellarCurrencyIssuerMapper.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/StellarCurrencyIssuerMapper.java new file mode 100644 index 0000000..25cc0b8 --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/mapper/StellarCurrencyIssuerMapper.java @@ -0,0 +1,26 @@ +package org.apache.fineract.cn.stellarbridge.service.internal.mapper; + +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; +import org.apache.fineract.cn.stellarbridge.service.internal.repository.StellarCurrencyIssuerEntity; + +public class StellarCurrencyIssuerMapper { + private StellarCurrencyIssuerMapper() { + super(); + } + + public static StellarCurrencyIssuer map(final StellarCurrencyIssuerEntity toMap) { + final StellarCurrencyIssuer ret = new StellarCurrencyIssuer(); + ret.setCurrencyCode(toMap.getCurrencyCode()); + ret.setStellarIssuer(toMap.getStellarIssuer()); + return ret; + } + + public static StellarCurrencyIssuerEntity map(final String tenantIdentifier, final StellarCurrencyIssuer toMap) { + final StellarCurrencyIssuerEntity ret = new StellarCurrencyIssuerEntity(); + ret.setTenantIdentifier(tenantIdentifier); + ret.setCurrencyCode(toMap.getCurrencyCode()); + ret.setStellarIssuer(toMap.getStellarIssuer()); + return ret; + } + +} diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/BridgeConfigurationEntity.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/BridgeConfigurationEntity.java index 9c582eb..66dbd99 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/BridgeConfigurationEntity.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/BridgeConfigurationEntity.java @@ -25,23 +25,22 @@ import javax.persistence.*; @Entity @Table(name = "nenet_configuration") public class BridgeConfigurationEntity { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "tenant_identifier") private String tenantIdentifier; - @Column(name = "fineract_incoming_identifier") - private String fineractIncomingAccountIdentifier; - @Column(name = "fineract_outgoing_identifier") - private String fineractOutgoingAccountIdentifier; - @Column(name = "fineract_staging_identifier") - private String fineractStagingAccountIdentifier; + @Column(name = "fineract_incoming_ledger") + private String fineractIncomingLedger; + @Column(name = "fineract_outgoing_ledger") + private String fineractOutgoingLedger; + @Column(name = "fineract_stellar_ledger") + private String fineractStellerLedger; @Column(name = "stellar_identifier") private String stellarAccountIdentifier; - @Column(name = "stellar_account_private_key") - private char[] stellarAccountPrivateKey; + @Column(name = "stellar_private_key") + private String stellarAccountPrivateKey; public BridgeConfigurationEntity() { super(); @@ -63,28 +62,28 @@ public class BridgeConfigurationEntity { this.tenantIdentifier = tenantIdentifier; } - public String getFineractIncomingAccountIdentifier() { - return fineractIncomingAccountIdentifier; + public String getFineractIncomingLedger() { + return fineractIncomingLedger; } - public void setFineractIncomingAccountIdentifier(String fineractIncomingAccountIdentifier) { - this.fineractIncomingAccountIdentifier = fineractIncomingAccountIdentifier; + public void setFineractIncomingLedger(String fineractIncomingLedger) { + this.fineractIncomingLedger = fineractIncomingLedger; } - public String getFineractOutgoingAccountIdentifier() { - return fineractOutgoingAccountIdentifier; + public String getFineractOutgoingLedger() { + return fineractOutgoingLedger; } - public void setFineractOutgoingAccountIdentifier(String fineractOutgoingAccountIdentifier) { - this.fineractOutgoingAccountIdentifier = fineractOutgoingAccountIdentifier; + public void setFineractOutgoingLedger(String fineractOutgoingLedger) { + this.fineractOutgoingLedger = fineractOutgoingLedger; } - public String getFineractStagingAccountIdentifier() { - return fineractStagingAccountIdentifier; + public String getFineractStellerLedger() { + return fineractStellerLedger; } - public void setFineractStagingAccountIdentifier(String fineractStagingAccountIdentifier) { - this.fineractStagingAccountIdentifier = fineractStagingAccountIdentifier; + public void setFineractStellerLedger(String fineractStellerLedger) { + this.fineractStellerLedger = fineractStellerLedger; } public String getStellarAccountIdentifier() { @@ -95,11 +94,11 @@ public class BridgeConfigurationEntity { this.stellarAccountIdentifier = stellarAccountIdentifier; } - public char[] getStellarAccountPrivateKey() { + public String getStellarAccountPrivateKey() { return stellarAccountPrivateKey; } - public void setStellarAccountPrivateKey(char[] stellarAccountPrivateKey) { + public void setStellarAccountPrivateKey(String stellarAccountPrivateKey) { this.stellarAccountPrivateKey = stellarAccountPrivateKey; } diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerEntity.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerEntity.java new file mode 100644 index 0000000..11c5a62 --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerEntity.java @@ -0,0 +1,80 @@ +package org.apache.fineract.cn.stellarbridge.service.internal.repository; + +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@SuppressWarnings("unused") +@Entity +@Table(name = "nenet_currency_issuer") +public class StellarCurrencyIssuerEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + @Column(name = "tenant_identifier") + private String tenantIdentifier; + @Column(name = "currency_code") + private String currencyCode; + @Column(name = "stellar_issuer") + private String stellarIssuer; + + public StellarCurrencyIssuerEntity() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTenantIdentifier() { + return tenantIdentifier; + } + + public void setTenantIdentifier(String tenantIdentifier) { + this.tenantIdentifier = tenantIdentifier; + } + + public String getCurrencyCode() { + return currencyCode; + } + + public void setCurrencyCode(String currencyCode) { + this.currencyCode = currencyCode; + } + + public String getStellarIssuer() { + return stellarIssuer; + } + + public void setStellarIssuer(String stellarIssuer) { + this.stellarIssuer = stellarIssuer; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StellarCurrencyIssuerEntity that = (StellarCurrencyIssuerEntity) o; + return Objects.equals(tenantIdentifier, that.tenantIdentifier) && + Objects.equals(currencyCode, that.currencyCode); + } + + @Override + public int hashCode() { + + return Objects.hash(tenantIdentifier, currencyCode); + } +} diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerRepository.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerRepository.java new file mode 100644 index 0000000..64499c0 --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCurrencyIssuerRepository.java @@ -0,0 +1,17 @@ +package org.apache.fineract.cn.stellarbridge.service.internal.repository; + +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface StellarCurrencyIssuerRepository extends + JpaRepository<StellarCurrencyIssuerEntity, Long> +{ + + Optional<StellarCurrencyIssuerEntity> findByTenantIdentifierAndCurrencyCode(String tenantIdentifier, String currencyCode); + + Stream<StellarCurrencyIssuerEntity> findByTenantIdentifier(String tenantIdentifier); +} diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCursorEntity.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCursorEntity.java index c69bc17..a546f8e 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCursorEntity.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/repository/StellarCursorEntity.java @@ -20,7 +20,7 @@ public class StellarCursorEntity { @Column(name = "id") private Long id; - @Column(name = "cursor") + @Column(name = "xcursor") private String cursor; @SuppressWarnings("unused") diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/service/StellarCurrencyIssuerService.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/service/StellarCurrencyIssuerService.java new file mode 100644 index 0000000..56d13b2 --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/internal/service/StellarCurrencyIssuerService.java @@ -0,0 +1,32 @@ +package org.apache.fineract.cn.stellarbridge.service.internal.service; + +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; +import org.apache.fineract.cn.stellarbridge.service.internal.mapper.StellarCurrencyIssuerMapper; +import org.apache.fineract.cn.stellarbridge.service.internal.repository.StellarCurrencyIssuerRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StellarCurrencyIssuerService { + + final private StellarCurrencyIssuerRepository stellarCurrencyIssuerRepository; + + @Autowired + public StellarCurrencyIssuerService( + final StellarCurrencyIssuerRepository stellarCurrencyIssuerRepository) { + this.stellarCurrencyIssuerRepository = stellarCurrencyIssuerRepository; + } + + public Optional<StellarCurrencyIssuer> find(final String tenantIdentifier, final String currencyCode) { + return this.stellarCurrencyIssuerRepository + .findByTenantIdentifierAndCurrencyCode(tenantIdentifier, currencyCode) + .map(StellarCurrencyIssuerMapper::map); + } + + public Stream<StellarCurrencyIssuer> findAll(final String tenantIdentifier) { + return this.stellarCurrencyIssuerRepository.findByTenantIdentifier(tenantIdentifier) + .map(StellarCurrencyIssuerMapper::map); + } +} diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/BridgeConfigurationRestController.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/BridgeConfigurationRestController.java index 6bfcc8d..3ae5e2a 100644 --- a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/BridgeConfigurationRestController.java +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/BridgeConfigurationRestController.java @@ -24,6 +24,7 @@ import javax.validation.Valid; import org.apache.fineract.cn.anubis.annotation.AcceptedTokenType; import org.apache.fineract.cn.anubis.annotation.Permittable; import org.apache.fineract.cn.command.gateway.CommandGateway; +import org.apache.fineract.cn.lang.ServiceException; import org.apache.fineract.cn.stellarbridge.api.v1.PermittableGroupIds; import org.apache.fineract.cn.stellarbridge.api.v1.domain.BridgeConfiguration; import org.apache.fineract.cn.stellarbridge.service.internal.command.ChangeConfigurationCommand; @@ -81,8 +82,9 @@ public class BridgeConfigurationRestController { @ResponseBody ResponseEntity<BridgeConfiguration> getBridgeConfiguration( @RequestHeader(TENANT_HEADER) final String tenantIdentifier) { - return ResponseEntity.ok(this.bridgeConfigurationService.findByTenantIdentifier(tenantIdentifier) - .orElseGet(() -> new BridgeConfiguration(null, null, null))); + return this.bridgeConfigurationService.findByTenantIdentifier(tenantIdentifier) + .map(ResponseEntity::ok) + .orElseThrow(() -> ServiceException.notFound("Tenant not found.")); } @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CONFIGURATION_MANAGEMENT) diff --git a/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/StellarCurrencyIssuerRestController.java b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/StellarCurrencyIssuerRestController.java new file mode 100644 index 0000000..d5507ed --- /dev/null +++ b/service/src/main/java/org/apache/fineract/cn/stellarbridge/service/rest/StellarCurrencyIssuerRestController.java @@ -0,0 +1,91 @@ +package org.apache.fineract.cn.stellarbridge.service.rest; + +import static org.apache.fineract.cn.lang.config.TenantHeaderFilter.TENANT_HEADER; + +import java.util.List; +import java.util.stream.Collectors; +import javax.validation.Valid; +import org.apache.fineract.cn.anubis.annotation.AcceptedTokenType; +import org.apache.fineract.cn.anubis.annotation.Permittable; +import org.apache.fineract.cn.command.gateway.CommandGateway; +import org.apache.fineract.cn.lang.ServiceException; +import org.apache.fineract.cn.stellarbridge.api.v1.PermittableGroupIds; +import org.apache.fineract.cn.stellarbridge.api.v1.domain.StellarCurrencyIssuer; +import org.apache.fineract.cn.stellarbridge.service.internal.command.ChangeStellarCurrencyIssuerCommand; +import org.apache.fineract.cn.stellarbridge.service.internal.service.StellarCurrencyIssuerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/currencyissuers") +public class StellarCurrencyIssuerRestController { + + private final CommandGateway commandGateway; + private final StellarCurrencyIssuerService stellarCurrencyIssuerService; + + @Autowired + public StellarCurrencyIssuerRestController( + final CommandGateway commandGateway, + final StellarCurrencyIssuerService stellarCurrencyIssuerService) { + this.commandGateway = commandGateway; + this.stellarCurrencyIssuerService = stellarCurrencyIssuerService; + } + @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CONFIGURATION_MANAGEMENT) + @RequestMapping( + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity<List<StellarCurrencyIssuer>> get( + @RequestHeader(TENANT_HEADER) final String tenantIdentifier) { + return ResponseEntity.ok(this.stellarCurrencyIssuerService.findAll(tenantIdentifier) + .collect(Collectors.toList())); + } + + @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CONFIGURATION_MANAGEMENT) + @RequestMapping( + value = "/{currencycode}", + method = RequestMethod.GET, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public + @ResponseBody + ResponseEntity<StellarCurrencyIssuer> getBridgeConfiguration( + @RequestHeader(TENANT_HEADER) final String tenantIdentifier, + @PathVariable("currencycode") String currencyCode) { + return this.stellarCurrencyIssuerService.find(tenantIdentifier, currencyCode) + .map(ResponseEntity::ok) + .orElseThrow(() -> ServiceException.notFound("Tenant not found.")); + } + + @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.CONFIGURATION_MANAGEMENT) + @RequestMapping( + value = "{currencycode}", + method = RequestMethod.PUT, + consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE + ) + public @ResponseBody ResponseEntity<Void> changeCase( + @RequestHeader(TENANT_HEADER) final String tenantIdentifier, + @PathVariable("currencycode") final String currencyCode, + @RequestBody @Valid final StellarCurrencyIssuer instance) + { + if (!currencyCode.equals(instance.getCurrencyCode())) + throw ServiceException.badRequest("Instance currency code may not be changed."); + + this.commandGateway.process(new ChangeStellarCurrencyIssuerCommand(tenantIdentifier, instance)); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } +} diff --git a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql index d5d568e..b51df47 100644 --- a/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql +++ b/service/src/main/resources/db/migrations/mariadb/V1__initial_setup.sql @@ -36,4 +36,13 @@ CREATE TABLE nenet_stellar_cursor ( created_on TIMESTAMP NOT NULL, CONSTRAINT nenet_stellar_cursor_uq UNIQUE (xcursor), CONSTRAINT nenet_stellar_cursor_pk PRIMARY KEY (id) +); + +CREATE TABLE nenet_currency_issuer ( + id BIGINT NOT NULL AUTO_INCREMENT, + tenant_identifier VARCHAR(32) NOT NULL, + currency_code VARCHAR(3) NOT NULL, + stellar_issuer VARCHAR(512) NULL, + CONSTRAINT nenet_currency_issuer_uq UNIQUE (tenant_identifier, currency_code), + CONSTRAINT nenet_currency_issuer_pk PRIMARY KEY (id) ); \ No newline at end of file
