From fb8fd3134f49f9951079d2e5a2a2a2bfa3bef51a Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Thu, 9 Dec 2021 12:35:01 +0100 Subject: [PATCH] add self payments endpoints --- application/build.gradle | 1 + backend/build.gradle | 1 + .../service/SelfPaymentsService.java | 25 +++++++ .../service/SelfPaymentsServiceTest.java | 42 +++++++++++ .../cotto/lndmanagej/grpc/GrpcInvoices.java | 5 +- .../lndmanagej/grpc/GrpcInvoicesTest.java | 3 +- .../SettledInvoicesRepositoryIT.java | 3 +- .../persistence/SettledInvoiceJpaDto.java | 10 +-- .../SettledSettledInvoiceJpaDtoTest.java | 7 +- .../de/cotto/lndmanagej/model/Payment.java | 4 ++ .../cotto/lndmanagej/model/SelfPayment.java | 24 +++++++ .../lndmanagej/model/SettledInvoice.java | 6 +- .../lndmanagej/model/PaymentHopTest.java | 8 +-- .../lndmanagej/model/PaymentRouteTest.java | 12 ++-- .../cotto/lndmanagej/model/PaymentTest.java | 12 +++- .../lndmanagej/model/SelfPaymentTest.java | 42 +++++++++++ .../lndmanagej/model/SettledInvoiceTest.java | 7 +- .../cotto/lndmanagej/SelfPaymentFixtures.java | 13 ++++ .../lndmanagej/model/PaymentFixtures.java | 22 ++++-- .../lndmanagej/model/PaymentHopFixtures.java | 8 ++- .../model/PaymentRouteFixtures.java | 17 +++-- .../model/SettledInvoiceFixtures.java | 25 +++++-- .../persistence/PaymentHopJpaDto.java | 2 +- .../payments/persistence/PaymentJpaDto.java | 2 +- .../persistence/PaymentRouteJpaDto.java | 4 +- .../persistence/PaymentHopJpaDtoTest.java | 7 +- .../persistence/PaymentRouteJpaDtoTest.java | 18 ++--- selfpayments/build.gradle | 12 ++++ .../lndmanagej/SpringBootConfiguration.java | 10 +++ .../persistence/SelfPaymentsRepositoryIT.java | 72 +++++++++++++++++++ .../selfpayments/SelfPaymentsDao.java | 14 ++++ .../persistence/SelfPaymentJpaDto.java | 14 ++++ .../persistence/SelfPaymentsDaoImpl.java | 38 ++++++++++ .../persistence/SelfPaymentsRepository.java | 46 ++++++++++++ .../persistence/SelfPaymentJpaDtoTest.java | 37 ++++++++++ .../persistence/SelfPaymentsDaoImplTest.java | 65 +++++++++++++++++ .../SelfPaymentsRepositoryTest.java | 13 ++++ settings.gradle | 1 + .../controller/SelfPaymentsControllerIT.java | 67 +++++++++++++++++ .../controller/SelfPaymentsController.java | 41 +++++++++++ .../dto/ObjectMapperConfiguration.java | 3 + .../controller/dto/SelfPaymentDto.java | 26 +++++++ .../SelfPaymentsControllerTest.java | 44 ++++++++++++ .../controller/dto/SelfPaymentDtoTest.java | 41 +++++++++++ 44 files changed, 808 insertions(+), 66 deletions(-) create mode 100644 backend/src/main/java/de/cotto/lndmanagej/service/SelfPaymentsService.java create mode 100644 backend/src/test/java/de/cotto/lndmanagej/service/SelfPaymentsServiceTest.java create mode 100644 model/src/main/java/de/cotto/lndmanagej/model/SelfPayment.java create mode 100644 model/src/test/java/de/cotto/lndmanagej/model/SelfPaymentTest.java create mode 100644 model/src/testFixtures/java/de/cotto/lndmanagej/SelfPaymentFixtures.java create mode 100644 selfpayments/build.gradle create mode 100644 selfpayments/src/integrationTest/java/de/cotto/lndmanagej/SpringBootConfiguration.java create mode 100644 selfpayments/src/integrationTest/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryIT.java create mode 100644 selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/SelfPaymentsDao.java create mode 100644 selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDto.java create mode 100644 selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImpl.java create mode 100644 selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepository.java create mode 100644 selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDtoTest.java create mode 100644 selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImplTest.java create mode 100644 selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryTest.java create mode 100644 web/src/integrationTest/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerIT.java create mode 100644 web/src/main/java/de/cotto/lndmanagej/controller/SelfPaymentsController.java create mode 100644 web/src/main/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDto.java create mode 100644 web/src/test/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerTest.java create mode 100644 web/src/test/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDtoTest.java diff --git a/application/build.gradle b/application/build.gradle index dadc33e4..d9c29b43 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -8,6 +8,7 @@ dependencies { runtimeOnly project(':invoices') runtimeOnly project(':payments') runtimeOnly project(':statistics') + runtimeOnly project(':selfpayments') runtimeOnly 'org.postgresql:postgresql' testRuntimeOnly 'com.h2database:h2' testImplementation project(':backend') diff --git a/backend/build.gradle b/backend/build.gradle index 67395069..54770591 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -8,6 +8,7 @@ dependencies { implementation project(':grpc-adapter') implementation project(':model') implementation project(':transactions') + implementation project(':selfpayments') testImplementation testFixtures(project(':model')) testImplementation testFixtures(project(':transactions')) } diff --git a/backend/src/main/java/de/cotto/lndmanagej/service/SelfPaymentsService.java b/backend/src/main/java/de/cotto/lndmanagej/service/SelfPaymentsService.java new file mode 100644 index 00000000..ddf05d1d --- /dev/null +++ b/backend/src/main/java/de/cotto/lndmanagej/service/SelfPaymentsService.java @@ -0,0 +1,25 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.SelfPayment; +import de.cotto.lndmanagej.selfpayments.SelfPaymentsDao; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class SelfPaymentsService { + private final SelfPaymentsDao dao; + + public SelfPaymentsService(SelfPaymentsDao dao) { + this.dao = dao; + } + + public List getSelfPaymentsToChannel(ChannelId channelId) { + return dao.getSelfPaymentsToChannel(channelId); + } + + public List getSelfPaymentsFromChannel(ChannelId channelId) { + return dao.getSelfPaymentsFromChannel(channelId).stream().distinct().toList(); + } +} diff --git a/backend/src/test/java/de/cotto/lndmanagej/service/SelfPaymentsServiceTest.java b/backend/src/test/java/de/cotto/lndmanagej/service/SelfPaymentsServiceTest.java new file mode 100644 index 00000000..1cc7900f --- /dev/null +++ b/backend/src/test/java/de/cotto/lndmanagej/service/SelfPaymentsServiceTest.java @@ -0,0 +1,42 @@ +package de.cotto.lndmanagej.service; + +import de.cotto.lndmanagej.selfpayments.SelfPaymentsDao; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SelfPaymentsServiceTest { + @InjectMocks + private SelfPaymentsService selfPaymentsService; + + @Mock + private SelfPaymentsDao selfPaymentsDao; + + @Test + void getSelfPaymentsToChannel() { + when(selfPaymentsDao.getSelfPaymentsToChannel(CHANNEL_ID)).thenReturn(List.of(SELF_PAYMENT)); + assertThat(selfPaymentsService.getSelfPaymentsToChannel(CHANNEL_ID)).containsExactly(SELF_PAYMENT); + } + + @Test + void getSelfPaymentsFromChannel() { + when(selfPaymentsDao.getSelfPaymentsFromChannel(CHANNEL_ID)).thenReturn(List.of(SELF_PAYMENT)); + assertThat(selfPaymentsService.getSelfPaymentsFromChannel(CHANNEL_ID)).containsExactly(SELF_PAYMENT); + } + + @Test + void getSelfPaymentsFromChannel_no_duplicates() { + when(selfPaymentsDao.getSelfPaymentsFromChannel(CHANNEL_ID)).thenReturn(List.of(SELF_PAYMENT, SELF_PAYMENT)); + assertThat(selfPaymentsService.getSelfPaymentsFromChannel(CHANNEL_ID)).containsExactly(SELF_PAYMENT); + } +} \ No newline at end of file diff --git a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcInvoices.java b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcInvoices.java index 8a64a33e..5215b5b0 100644 --- a/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcInvoices.java +++ b/grpc-adapter/src/main/java/de/cotto/lndmanagej/grpc/GrpcInvoices.java @@ -10,7 +10,6 @@ import lnrpc.ListInvoiceResponse; import org.springframework.stereotype.Component; import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.HexFormat; import java.util.Iterator; import java.util.List; @@ -19,6 +18,8 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import static java.time.ZoneOffset.UTC; + @Component public class GrpcInvoices { private static final int LIMIT = 1_000; @@ -62,7 +63,7 @@ public class GrpcInvoices { return new SettledInvoice( lndInvoice.getAddIndex(), lndInvoice.getSettleIndex(), - LocalDateTime.ofEpochSecond(lndInvoice.getSettleDate(), 0, ZoneOffset.UTC), + LocalDateTime.ofEpochSecond(lndInvoice.getSettleDate(), 0, UTC).atZone(UTC), HEX_FORMAT.formatHex(lndInvoice.getRHash().toByteArray()), Coins.ofMilliSatoshis(lndInvoice.getAmtPaidMsat()), lndInvoice.getMemo(), diff --git a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcInvoicesTest.java b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcInvoicesTest.java index 18970ff5..9ddb3a57 100644 --- a/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcInvoicesTest.java +++ b/grpc-adapter/src/test/java/de/cotto/lndmanagej/grpc/GrpcInvoicesTest.java @@ -13,7 +13,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import javax.annotation.Nullable; -import java.time.ZoneOffset; import java.util.Collections; import java.util.HexFormat; import java.util.LinkedHashMap; @@ -250,7 +249,7 @@ class GrpcInvoicesTest { .setSettleIndex(settledInvoice.settleIndex()) .setRHash(ByteString.copyFrom(HEX_FORMAT.parseHex(settledInvoice.hash()))) .setMemo(settledInvoice.memo()) - .setSettleDate(settledInvoice.settleDate().toEpochSecond(ZoneOffset.UTC)) + .setSettleDate(settledInvoice.settleDate().toEpochSecond()) .setAmtPaidMsat(settledInvoice.amountPaid().milliSatoshis()) .addHtlcs(htlc) .build(); diff --git a/invoices/src/integrationTest/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoicesRepositoryIT.java b/invoices/src/integrationTest/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoicesRepositoryIT.java index cb9824cb..e92c9f0b 100644 --- a/invoices/src/integrationTest/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoicesRepositoryIT.java +++ b/invoices/src/integrationTest/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoicesRepositoryIT.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Optional; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; @@ -59,7 +60,7 @@ class SettledInvoicesRepositoryIT { return SettledInvoiceJpaDto.createFromModel(new SettledInvoice( addIndex, settleIndex, - LocalDateTime.MIN, + LocalDateTime.MIN.atZone(ZoneOffset.UTC), "", Coins.NONE, "", diff --git a/invoices/src/main/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoiceJpaDto.java b/invoices/src/main/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoiceJpaDto.java index a612e63c..3251cc2c 100644 --- a/invoices/src/main/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoiceJpaDto.java +++ b/invoices/src/main/java/de/cotto/lndmanagej/invoices/persistence/SettledInvoiceJpaDto.java @@ -11,10 +11,12 @@ import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.Table; import java.time.LocalDateTime; -import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Objects; import java.util.Optional; +import static java.time.ZoneOffset.UTC; + @Entity @Table(name = "settled_invoices", indexes = {@Index(unique = true, columnList = "settleIndex")}) public class SettledInvoiceJpaDto { @@ -47,7 +49,7 @@ public class SettledInvoiceJpaDto { SettledInvoiceJpaDto jpaDto = new SettledInvoiceJpaDto(); jpaDto.addIndex = settledInvoice.addIndex(); jpaDto.settleIndex = settledInvoice.settleIndex(); - jpaDto.settleDate = settledInvoice.settleDate().toEpochSecond(ZoneOffset.UTC); + jpaDto.settleDate = settledInvoice.settleDate().toEpochSecond(); jpaDto.hash = settledInvoice.hash(); jpaDto.amountPaid = settledInvoice.amountPaid().milliSatoshis(); jpaDto.memo = settledInvoice.memo(); @@ -57,7 +59,7 @@ public class SettledInvoiceJpaDto { } public SettledInvoice toModel() { - LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(settleDate, 0, ZoneOffset.UTC); + ZonedDateTime dateTime = LocalDateTime.ofEpochSecond(settleDate, 0, UTC).atZone(UTC); Optional channelId; if (receivedVia > 0) { channelId = Optional.of(ChannelId.fromShortChannelId(receivedVia)); @@ -67,7 +69,7 @@ public class SettledInvoiceJpaDto { return new SettledInvoice( addIndex, settleIndex, - localDateTime, + dateTime, Objects.requireNonNull(hash), Coins.ofMilliSatoshis(amountPaid), Objects.requireNonNull(memo), diff --git a/invoices/src/test/java/de/cotto/lndmanagej/invoices/persistence/SettledSettledInvoiceJpaDtoTest.java b/invoices/src/test/java/de/cotto/lndmanagej/invoices/persistence/SettledSettledInvoiceJpaDtoTest.java index 4a30f301..1ee7d803 100644 --- a/invoices/src/test/java/de/cotto/lndmanagej/invoices/persistence/SettledSettledInvoiceJpaDtoTest.java +++ b/invoices/src/test/java/de/cotto/lndmanagej/invoices/persistence/SettledSettledInvoiceJpaDtoTest.java @@ -4,10 +4,10 @@ import de.cotto.lndmanagej.model.SettledInvoice; import de.cotto.lndmanagej.model.SettledInvoiceFixtures; import org.junit.jupiter.api.Test; -import java.time.ZoneOffset; import java.util.Optional; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.KEYSEND_MESSAGE; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_KEYSEND; @@ -21,13 +21,12 @@ class SettledSettledInvoiceJpaDtoTest { SettledInvoiceJpaDto jpaDto = SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE); assertThat(jpaDto.getAddIndex()).isEqualTo(SETTLED_INVOICE.addIndex()); assertThat(jpaDto.getSettleIndex()).isEqualTo(SETTLED_INVOICE.settleIndex()); - assertThat(jpaDto.getSettleDate()) - .isEqualTo(SETTLED_INVOICE.settleDate().toEpochSecond(ZoneOffset.UTC)); + assertThat(jpaDto.getSettleDate()).isEqualTo(SETTLED_INVOICE.settleDate().toEpochSecond()); assertThat(jpaDto.getHash()).isEqualTo(SETTLED_INVOICE.hash()); assertThat(jpaDto.getAmountPaid()).isEqualTo(SETTLED_INVOICE.amountPaid().milliSatoshis()); assertThat(jpaDto.getMemo()).isEqualTo(SETTLED_INVOICE.memo()); assertThat(jpaDto.getKeysendMessage()).isNull(); - assertThat(jpaDto.getReceivedVia()).isEqualTo(CHANNEL_ID.getShortChannelId()); + assertThat(jpaDto.getReceivedVia()).isEqualTo(CHANNEL_ID_2.getShortChannelId()); } @Test diff --git a/model/src/main/java/de/cotto/lndmanagej/model/Payment.java b/model/src/main/java/de/cotto/lndmanagej/model/Payment.java index 3f88aa58..aad9e0d3 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/Payment.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/Payment.java @@ -2,6 +2,7 @@ package de.cotto.lndmanagej.model; import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; public record Payment( long index, @@ -11,4 +12,7 @@ public record Payment( Coins fees, List routes ) { + public Optional getFirstChannel() { + return routes.stream().flatMap(route -> route.hops().stream()).map(PaymentHop::channelId).findFirst(); + } } diff --git a/model/src/main/java/de/cotto/lndmanagej/model/SelfPayment.java b/model/src/main/java/de/cotto/lndmanagej/model/SelfPayment.java new file mode 100644 index 00000000..5636f6d4 --- /dev/null +++ b/model/src/main/java/de/cotto/lndmanagej/model/SelfPayment.java @@ -0,0 +1,24 @@ +package de.cotto.lndmanagej.model; + +import java.time.ZonedDateTime; +import java.util.Optional; + +public record SelfPayment( + String memo, + ZonedDateTime settleDate, + Coins value, + Coins fees, + Optional firstChannel, + Optional lastChannel +) { + public SelfPayment(Payment payment, SettledInvoice settledInvoice) { + this( + settledInvoice.memo(), + settledInvoice.settleDate(), + payment.value(), + payment.fees(), + payment.getFirstChannel(), + settledInvoice.receivedVia() + ); + } +} diff --git a/model/src/main/java/de/cotto/lndmanagej/model/SettledInvoice.java b/model/src/main/java/de/cotto/lndmanagej/model/SettledInvoice.java index 4049aea9..d892c520 100644 --- a/model/src/main/java/de/cotto/lndmanagej/model/SettledInvoice.java +++ b/model/src/main/java/de/cotto/lndmanagej/model/SettledInvoice.java @@ -1,12 +1,14 @@ package de.cotto.lndmanagej.model; import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Optional; public record SettledInvoice( long addIndex, long settleIndex, - LocalDateTime settleDate, + ZonedDateTime settleDate, String hash, Coins amountPaid, String memo, @@ -16,7 +18,7 @@ public record SettledInvoice( public static final SettledInvoice INVALID = new SettledInvoice( -1, -1, - LocalDateTime.MIN, + LocalDateTime.MIN.atZone(ZoneOffset.UTC), "", Coins.NONE, "", diff --git a/model/src/test/java/de/cotto/lndmanagej/model/PaymentHopTest.java b/model/src/test/java/de/cotto/lndmanagej/model/PaymentHopTest.java index c0754371..3619246f 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/PaymentHopTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/PaymentHopTest.java @@ -2,18 +2,18 @@ package de.cotto.lndmanagej.model; import org.junit.jupiter.api.Test; -import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_4; import static org.assertj.core.api.Assertions.assertThat; class PaymentHopTest { @Test void channelId() { - assertThat(PAYMENT_HOP.channelId()).isEqualTo(CHANNEL_ID); + assertThat(PAYMENT_HOP_CHANNEL_4.channelId()).isEqualTo(CHANNEL_ID_4); } @Test void amount() { - assertThat(PAYMENT_HOP.amount()).isEqualTo(Coins.ofSatoshis(1)); + assertThat(PAYMENT_HOP_CHANNEL_4.amount()).isEqualTo(Coins.ofSatoshis(4)); } } \ No newline at end of file diff --git a/model/src/test/java/de/cotto/lndmanagej/model/PaymentRouteTest.java b/model/src/test/java/de/cotto/lndmanagej/model/PaymentRouteTest.java index af8c2a54..c58741c5 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/PaymentRouteTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/PaymentRouteTest.java @@ -4,16 +4,18 @@ import org.junit.jupiter.api.Test; import java.util.List; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_2; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_3; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_2; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_3; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_4; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_4_TO_2; import static org.assertj.core.api.Assertions.assertThat; class PaymentRouteTest { @Test void hops() { - assertThat(PAYMENT_ROUTE.hops()).isEqualTo(List.of(PAYMENT_HOP, PAYMENT_HOP_2, PAYMENT_HOP_3)); + assertThat(PAYMENT_ROUTE_4_TO_2.hops()).isEqualTo( + List.of(PAYMENT_HOP_CHANNEL_4, PAYMENT_HOP_CHANNEL_3, PAYMENT_HOP_CHANNEL_2) + ); } } \ No newline at end of file diff --git a/model/src/test/java/de/cotto/lndmanagej/model/PaymentTest.java b/model/src/test/java/de/cotto/lndmanagej/model/PaymentTest.java index 7e6677cd..ab1434e3 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/PaymentTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/PaymentTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.util.List; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_2; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_CREATION_DATE_TIME; @@ -11,8 +12,8 @@ import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_FEES; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_HASH; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_INDEX; import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_VALUE; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_2; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_3_TO_1; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_4_TO_1; import static org.assertj.core.api.Assertions.assertThat; class PaymentTest { @@ -43,6 +44,11 @@ class PaymentTest { @Test void routes() { - assertThat(PAYMENT_2.routes()).isEqualTo(List.of(PAYMENT_ROUTE, PAYMENT_ROUTE_2)); + assertThat(PAYMENT_2.routes()).isEqualTo(List.of(PAYMENT_ROUTE_4_TO_1, PAYMENT_ROUTE_3_TO_1)); + } + + @Test + void getFirstChannel() { + assertThat(PAYMENT.getFirstChannel()).contains(CHANNEL_ID_4); } } \ No newline at end of file diff --git a/model/src/test/java/de/cotto/lndmanagej/model/SelfPaymentTest.java b/model/src/test/java/de/cotto/lndmanagej/model/SelfPaymentTest.java new file mode 100644 index 00000000..be197a54 --- /dev/null +++ b/model/src/test/java/de/cotto/lndmanagej/model/SelfPaymentTest.java @@ -0,0 +1,42 @@ +package de.cotto.lndmanagej.model; + +import org.junit.jupiter.api.Test; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT_2; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_2; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_2; +import static org.assertj.core.api.Assertions.assertThat; + +class SelfPaymentTest { + @Test + void memo() { + assertThat(SELF_PAYMENT.memo()).isEqualTo(SETTLED_INVOICE.memo()); + } + + @Test + void settleDate() { + assertThat(SELF_PAYMENT_2.settleDate()).isEqualTo(SETTLED_INVOICE_2.settleDate()); + } + + @Test + void value() { + assertThat(SELF_PAYMENT_2.value()).isEqualTo(PAYMENT_2.value()); + } + + @Test + void fees() { + assertThat(SELF_PAYMENT_2.fees()).isEqualTo(PAYMENT_2.fees()); + } + + @Test + void firstChannel() { + assertThat(SELF_PAYMENT_2.firstChannel()).isEqualTo(PAYMENT_2.getFirstChannel()); + } + + @Test + void lastChannel() { + assertThat(SELF_PAYMENT_2.lastChannel()).isEqualTo(SETTLED_INVOICE_2.receivedVia()); + } +} \ No newline at end of file diff --git a/model/src/test/java/de/cotto/lndmanagej/model/SettledInvoiceTest.java b/model/src/test/java/de/cotto/lndmanagej/model/SettledInvoiceTest.java index e2b366b7..597c267a 100644 --- a/model/src/test/java/de/cotto/lndmanagej/model/SettledInvoiceTest.java +++ b/model/src/test/java/de/cotto/lndmanagej/model/SettledInvoiceTest.java @@ -3,9 +3,10 @@ package de.cotto.lndmanagej.model; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Optional; -import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.ADD_INDEX; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.AMOUNT_PAID; import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.HASH; @@ -23,7 +24,7 @@ class SettledInvoiceTest { SettledInvoice expected = new SettledInvoice( -1, -1, - LocalDateTime.MIN, + LocalDateTime.MIN.atZone(ZoneOffset.UTC), "", Coins.NONE, "", @@ -81,6 +82,6 @@ class SettledInvoiceTest { @Test void receivedVia() { - assertThat(SETTLED_INVOICE.receivedVia()).contains(CHANNEL_ID); + assertThat(SETTLED_INVOICE.receivedVia()).contains(CHANNEL_ID_2); } } \ No newline at end of file diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/SelfPaymentFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/SelfPaymentFixtures.java new file mode 100644 index 00000000..7efa07a9 --- /dev/null +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/SelfPaymentFixtures.java @@ -0,0 +1,13 @@ +package de.cotto.lndmanagej; + +import de.cotto.lndmanagej.model.SelfPayment; + +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_2; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_2; + +public class SelfPaymentFixtures { + public static final SelfPayment SELF_PAYMENT = new SelfPayment(PAYMENT, SETTLED_INVOICE); + public static final SelfPayment SELF_PAYMENT_2 = new SelfPayment(PAYMENT_2, SETTLED_INVOICE_2); +} diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentFixtures.java index cab4f94d..f2f40acf 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentFixtures.java @@ -3,25 +3,33 @@ package de.cotto.lndmanagej.model; import java.time.LocalDateTime; import java.util.List; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_2; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_3_TO_1; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_4_TO_1; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_4_TO_2; public class PaymentFixtures { public static final int PAYMENT_INDEX = 2; public static final int PAYMENT_INDEX_2 = 3; - public static final String PAYMENT_HASH = "abc"; + public static final int PAYMENT_INDEX_3 = 4; + public static final String PAYMENT_HASH = "1234"; + public static final String PAYMENT_HASH_2 = "aaa0"; + public static final String PAYMENT_HASH_3 = "aaa1"; public static final Coins PAYMENT_VALUE = Coins.ofSatoshis(1_000_000); public static final Coins PAYMENT_FEES = Coins.ofMilliSatoshis(10); - private static final List ONE_ROUTE = List.of(PAYMENT_ROUTE); - private static final List TWO_ROUTES = List.of(PAYMENT_ROUTE, PAYMENT_ROUTE_2); + private static final List ONE_ROUTE_4_TO_2 = List.of(PAYMENT_ROUTE_4_TO_2); + private static final List TWO_ROUTES = List.of(PAYMENT_ROUTE_4_TO_1, PAYMENT_ROUTE_3_TO_1); public static final LocalDateTime PAYMENT_CREATION_DATE_TIME = LocalDateTime.of(2021, 12, 5, 22, 22, 22, 500_000_000); public static final Payment PAYMENT = new Payment( - PAYMENT_INDEX, PAYMENT_HASH, PAYMENT_CREATION_DATE_TIME, PAYMENT_VALUE, PAYMENT_FEES, ONE_ROUTE + PAYMENT_INDEX, PAYMENT_HASH, PAYMENT_CREATION_DATE_TIME, PAYMENT_VALUE, PAYMENT_FEES, ONE_ROUTE_4_TO_2 ); public static final Payment PAYMENT_2 = new Payment( - PAYMENT_INDEX_2, PAYMENT_HASH, PAYMENT_CREATION_DATE_TIME, PAYMENT_VALUE, PAYMENT_FEES, TWO_ROUTES + PAYMENT_INDEX_2, PAYMENT_HASH_2, PAYMENT_CREATION_DATE_TIME, PAYMENT_VALUE, PAYMENT_FEES, TWO_ROUTES + ); + + public static final Payment PAYMENT_3 = new Payment( + PAYMENT_INDEX_3, PAYMENT_HASH_3, PAYMENT_CREATION_DATE_TIME, PAYMENT_VALUE, PAYMENT_FEES, TWO_ROUTES ); } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentHopFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentHopFixtures.java index 29ba1604..6fcaddce 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentHopFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentHopFixtures.java @@ -3,9 +3,11 @@ package de.cotto.lndmanagej.model; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4; public class PaymentHopFixtures { - public static final PaymentHop PAYMENT_HOP = new PaymentHop(CHANNEL_ID, Coins.ofSatoshis(1)); - public static final PaymentHop PAYMENT_HOP_2 = new PaymentHop(CHANNEL_ID_2, Coins.ofSatoshis(2)); - public static final PaymentHop PAYMENT_HOP_3 = new PaymentHop(CHANNEL_ID_3, Coins.ofSatoshis(3)); + public static final PaymentHop PAYMENT_HOP_CHANNEL_1 = new PaymentHop(CHANNEL_ID, Coins.ofSatoshis(1)); + public static final PaymentHop PAYMENT_HOP_CHANNEL_2 = new PaymentHop(CHANNEL_ID_2, Coins.ofSatoshis(2)); + public static final PaymentHop PAYMENT_HOP_CHANNEL_3 = new PaymentHop(CHANNEL_ID_3, Coins.ofSatoshis(3)); + public static final PaymentHop PAYMENT_HOP_CHANNEL_4 = new PaymentHop(CHANNEL_ID_4, Coins.ofSatoshis(4)); } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentRouteFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentRouteFixtures.java index d0536e1d..92da5d2f 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentRouteFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/PaymentRouteFixtures.java @@ -2,13 +2,16 @@ package de.cotto.lndmanagej.model; import java.util.List; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_2; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_3; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_1; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_2; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_3; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_4; public class PaymentRouteFixtures { - public static final PaymentRoute PAYMENT_ROUTE = - new PaymentRoute(List.of(PAYMENT_HOP, PAYMENT_HOP_2, PAYMENT_HOP_3)); - public static final PaymentRoute PAYMENT_ROUTE_2 = - new PaymentRoute(List.of(PAYMENT_HOP, PAYMENT_HOP_2)); + public static final PaymentRoute PAYMENT_ROUTE_4_TO_2 = + new PaymentRoute(List.of(PAYMENT_HOP_CHANNEL_4, PAYMENT_HOP_CHANNEL_3, PAYMENT_HOP_CHANNEL_2)); + public static final PaymentRoute PAYMENT_ROUTE_4_TO_1 = + new PaymentRoute(List.of(PAYMENT_HOP_CHANNEL_4, PAYMENT_HOP_CHANNEL_3, PAYMENT_HOP_CHANNEL_1)); + public static final PaymentRoute PAYMENT_ROUTE_3_TO_1 = + new PaymentRoute(List.of(PAYMENT_HOP_CHANNEL_3, PAYMENT_HOP_CHANNEL_1)); } diff --git a/model/src/testFixtures/java/de/cotto/lndmanagej/model/SettledInvoiceFixtures.java b/model/src/testFixtures/java/de/cotto/lndmanagej/model/SettledInvoiceFixtures.java index db138522..ee342479 100644 --- a/model/src/testFixtures/java/de/cotto/lndmanagej/model/SettledInvoiceFixtures.java +++ b/model/src/testFixtures/java/de/cotto/lndmanagej/model/SettledInvoiceFixtures.java @@ -1,18 +1,24 @@ package de.cotto.lndmanagej.model; -import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Optional; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; public class SettledInvoiceFixtures { - public static final LocalDateTime SETTLE_DATE = LocalDateTime.of(2021, 12, 2, 16, 4, 30); + public static final ZonedDateTime SETTLE_DATE = ZonedDateTime.of(2021, 12, 2, 16, 4, 30, 0, ZoneOffset.UTC); public static final int ADD_INDEX = 2; public static final int ADD_INDEX_2 = 1; + public static final int ADD_INDEX_3 = 3; public static final int SETTLE_INDEX = 1; public static final int SETTLE_INDEX_2 = 2; + public static final int SETTLE_INDEX_3 = 3; public static final String HASH = "1234"; public static final String HASH_2 = "aaa0"; + public static final String HASH_3 = "010101"; public static final Coins AMOUNT_PAID = Coins.ofMilliSatoshis(123); public static final Coins AMOUNT_PAID_2 = Coins.ofMilliSatoshis(4_567); public static final String MEMO = "this is a memo"; @@ -27,7 +33,7 @@ public class SettledInvoiceFixtures { AMOUNT_PAID, MEMO, Optional.empty(), - Optional.of(CHANNEL_ID) + Optional.of(CHANNEL_ID_2) ); public static final SettledInvoice SETTLED_INVOICE_NO_CHANNEL_ID = new SettledInvoice( @@ -49,7 +55,7 @@ public class SettledInvoiceFixtures { AMOUNT_PAID, MEMO, Optional.of(KEYSEND_MESSAGE), - Optional.of(CHANNEL_ID) + Optional.of(CHANNEL_ID_2) ); public static final SettledInvoice SETTLED_INVOICE_2 = new SettledInvoice( @@ -62,4 +68,15 @@ public class SettledInvoiceFixtures { Optional.empty(), Optional.of(CHANNEL_ID) ); + + public static final SettledInvoice SETTLED_INVOICE_3 = new SettledInvoice( + ADD_INDEX_3, + SETTLE_INDEX_3, + SETTLE_DATE.plusSeconds(2), + HASH_3, + AMOUNT_PAID_2, + MEMO_2, + Optional.empty(), + Optional.of(CHANNEL_ID_3) + ); } diff --git a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDto.java b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDto.java index 0c51a0c3..08dd43a5 100644 --- a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDto.java +++ b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDto.java @@ -7,7 +7,7 @@ import de.cotto.lndmanagej.model.PaymentHop; import javax.persistence.Embeddable; @Embeddable -public class PaymentHopJpaDto { +class PaymentHopJpaDto { private long channelId; private long amount; diff --git a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentJpaDto.java b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentJpaDto.java index b056f89f..1a733d63 100644 --- a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentJpaDto.java +++ b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentJpaDto.java @@ -16,7 +16,7 @@ import java.util.Objects; @Entity @Table(name = "payments") -class PaymentJpaDto { +public class PaymentJpaDto { @Id private long paymentIndex; diff --git a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDto.java b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDto.java index 307ea9fc..afbcd20d 100644 --- a/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDto.java +++ b/payments/src/main/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDto.java @@ -9,19 +9,21 @@ import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.OrderColumn; import javax.persistence.Table; import java.util.List; import java.util.Objects; @Entity @Table(name = "payment_routes") -public class PaymentRouteJpaDto { +class PaymentRouteJpaDto { @Id @GeneratedValue @SuppressWarnings("unused") private long routeId; @Nullable + @OrderColumn @ElementCollection @CollectionTable(name = "payment_route_hops") private List hops; diff --git a/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDtoTest.java b/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDtoTest.java index 225edc4e..b0d7f347 100644 --- a/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDtoTest.java +++ b/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentHopJpaDtoTest.java @@ -4,18 +4,19 @@ import de.cotto.lndmanagej.model.Coins; import org.junit.jupiter.api.Test; import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_1; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_4; import static org.assertj.core.api.Assertions.assertThat; class PaymentHopJpaDtoTest { @Test void toModel() { assertThat(new PaymentHopJpaDto(CHANNEL_ID.getShortChannelId(), Coins.ofSatoshis(1).milliSatoshis()).toModel()) - .isEqualTo(PAYMENT_HOP); + .isEqualTo(PAYMENT_HOP_CHANNEL_1); } @Test void createFromModel() { - assertThat(PaymentHopJpaDto.createFromModel(PAYMENT_HOP).toModel()).isEqualTo(PAYMENT_HOP); + assertThat(PaymentHopJpaDto.createFromModel(PAYMENT_HOP_CHANNEL_4).toModel()).isEqualTo(PAYMENT_HOP_CHANNEL_4); } } \ No newline at end of file diff --git a/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDtoTest.java b/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDtoTest.java index a2dc374c..43118b4b 100644 --- a/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDtoTest.java +++ b/payments/src/test/java/de/cotto/lndmanagej/payments/persistence/PaymentRouteJpaDtoTest.java @@ -4,10 +4,10 @@ import org.junit.jupiter.api.Test; import java.util.List; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_2; -import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_3; -import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_2; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_3; +import static de.cotto.lndmanagej.model.PaymentHopFixtures.PAYMENT_HOP_CHANNEL_4; +import static de.cotto.lndmanagej.model.PaymentRouteFixtures.PAYMENT_ROUTE_4_TO_2; import static org.assertj.core.api.Assertions.assertThat; class PaymentRouteJpaDtoTest { @@ -15,15 +15,15 @@ class PaymentRouteJpaDtoTest { void toModel() { assertThat(new PaymentRouteJpaDto( List.of( - PaymentHopJpaDto.createFromModel(PAYMENT_HOP), - PaymentHopJpaDto.createFromModel(PAYMENT_HOP_2), - PaymentHopJpaDto.createFromModel(PAYMENT_HOP_3) + PaymentHopJpaDto.createFromModel(PAYMENT_HOP_CHANNEL_4), + PaymentHopJpaDto.createFromModel(PAYMENT_HOP_CHANNEL_3), + PaymentHopJpaDto.createFromModel(PAYMENT_HOP_CHANNEL_2) ) - ).toModel()).isEqualTo(PAYMENT_ROUTE); + ).toModel()).isEqualTo(PAYMENT_ROUTE_4_TO_2); } @Test void createFromModel() { - assertThat(PaymentRouteJpaDto.createFromModel(PAYMENT_ROUTE).toModel()).isEqualTo(PAYMENT_ROUTE); + assertThat(PaymentRouteJpaDto.createFromModel(PAYMENT_ROUTE_4_TO_2).toModel()).isEqualTo(PAYMENT_ROUTE_4_TO_2); } } \ No newline at end of file diff --git a/selfpayments/build.gradle b/selfpayments/build.gradle new file mode 100644 index 00000000..6b676476 --- /dev/null +++ b/selfpayments/build.gradle @@ -0,0 +1,12 @@ +plugins { + id 'lnd-manageJ.java-library-conventions' +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation project(':model') + implementation project(':invoices') + implementation project(':payments') + testRuntimeOnly 'com.h2database:h2' + testFixturesApi testFixtures(project(':model')) +} \ No newline at end of file diff --git a/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/SpringBootConfiguration.java b/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/SpringBootConfiguration.java new file mode 100644 index 00000000..7b1634de --- /dev/null +++ b/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/SpringBootConfiguration.java @@ -0,0 +1,10 @@ +package de.cotto.lndmanagej; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringBootConfiguration { + public SpringBootConfiguration() { + // default constructor + } +} diff --git a/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryIT.java b/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryIT.java new file mode 100644 index 00000000..35c40311 --- /dev/null +++ b/selfpayments/src/integrationTest/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryIT.java @@ -0,0 +1,72 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import de.cotto.lndmanagej.invoices.persistence.SettledInvoiceJpaDto; +import de.cotto.lndmanagej.invoices.persistence.SettledInvoicesRepository; +import de.cotto.lndmanagej.payments.persistence.PaymentJpaDto; +import de.cotto.lndmanagej.payments.persistence.PaymentsRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_3; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_2; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_3; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_2; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_3; +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class SelfPaymentsRepositoryIT { + @Autowired + private SelfPaymentsRepository repository; + + @Autowired + private SettledInvoicesRepository invoicesRepository; + + @Autowired + private PaymentsRepository paymentsRepository; + + @BeforeEach + void setUp() { + paymentsRepository.save(PaymentJpaDto.createFromModel(PAYMENT)); + paymentsRepository.save(PaymentJpaDto.createFromModel(PAYMENT_2)); + paymentsRepository.save(PaymentJpaDto.createFromModel(PAYMENT_3)); + invoicesRepository.save(SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE)); + invoicesRepository.save(SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE_2)); + invoicesRepository.save(SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE_3)); + } + + @Test + void getSelfPayments() { + assertThat(repository.getAllSelfPayments()).map(SelfPaymentJpaDto::toModel) + .containsExactlyInAnyOrder(SELF_PAYMENT, SELF_PAYMENT_2); + } + + @Test + void getSelfPaymentsToChannel() { + assertThat(repository.getSelfPaymentsToChannel(CHANNEL_ID_2.getShortChannelId())) + .map(SelfPaymentJpaDto::toModel) + .containsExactly(SELF_PAYMENT); + } + + @Test + void getSelfPaymentsFromChannel() { + assertThat(repository.getSelfPaymentsFromChannel(CHANNEL_ID_3.getShortChannelId())) + .map(SelfPaymentJpaDto::toModel) + .containsExactly(SELF_PAYMENT_2); + } + + @Test + void getSelfPaymentsFromChannel_two_payments() { + assertThat(repository.getSelfPaymentsFromChannel(CHANNEL_ID_4.getShortChannelId())) + .map(SelfPaymentJpaDto::toModel) + .containsExactly(SELF_PAYMENT, SELF_PAYMENT_2); + } +} \ No newline at end of file diff --git a/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/SelfPaymentsDao.java b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/SelfPaymentsDao.java new file mode 100644 index 00000000..24d730c9 --- /dev/null +++ b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/SelfPaymentsDao.java @@ -0,0 +1,14 @@ +package de.cotto.lndmanagej.selfpayments; + +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.SelfPayment; + +import java.util.List; + +public interface SelfPaymentsDao { + List getAllSelfPayments(); + + List getSelfPaymentsToChannel(ChannelId channelId); + + List getSelfPaymentsFromChannel(ChannelId channelId); +} diff --git a/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDto.java b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDto.java new file mode 100644 index 00000000..a11b79e3 --- /dev/null +++ b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDto.java @@ -0,0 +1,14 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import de.cotto.lndmanagej.invoices.persistence.SettledInvoiceJpaDto; +import de.cotto.lndmanagej.model.SelfPayment; +import de.cotto.lndmanagej.payments.persistence.PaymentJpaDto; + +public record SelfPaymentJpaDto( + PaymentJpaDto payment, + SettledInvoiceJpaDto settledInvoice +) { + public SelfPayment toModel() { + return new SelfPayment(payment.toModel(), settledInvoice.toModel()); + } +} diff --git a/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImpl.java b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImpl.java new file mode 100644 index 00000000..8909fc38 --- /dev/null +++ b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImpl.java @@ -0,0 +1,38 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.SelfPayment; +import de.cotto.lndmanagej.selfpayments.SelfPaymentsDao; +import org.springframework.stereotype.Component; + +import javax.transaction.Transactional; +import java.util.List; + +@Component +@Transactional +public class SelfPaymentsDaoImpl implements SelfPaymentsDao { + private final SelfPaymentsRepository repository; + + public SelfPaymentsDaoImpl(SelfPaymentsRepository repository) { + this.repository = repository; + } + + @Override + public List getAllSelfPayments() { + return toModel(repository.getAllSelfPayments()); + } + + @Override + public List getSelfPaymentsToChannel(ChannelId channelId) { + return toModel(repository.getSelfPaymentsToChannel(channelId.getShortChannelId())); + } + + @Override + public List getSelfPaymentsFromChannel(ChannelId channelId) { + return toModel(repository.getSelfPaymentsFromChannel(channelId.getShortChannelId())); + } + + private List toModel(List selfPayments) { + return selfPayments.stream().map(SelfPaymentJpaDto::toModel).toList(); + } +} diff --git a/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepository.java b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepository.java new file mode 100644 index 00000000..6b10d089 --- /dev/null +++ b/selfpayments/src/main/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepository.java @@ -0,0 +1,46 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.List; + +public interface SelfPaymentsRepository extends JpaRepository { + @Query("SELECT NEW de.cotto.lndmanagej.selfpayments.persistence.SelfPaymentJpaDto(p, i) " + + "FROM PaymentJpaDto p " + + "JOIN SettledInvoiceJpaDto i ON p.hash = i.hash " + + "ORDER BY i.settleDate ASC") + List getAllSelfPayments(); + + @Query("SELECT NEW de.cotto.lndmanagej.selfpayments.persistence.SelfPaymentJpaDto(p, i) " + + "FROM PaymentJpaDto p " + + "JOIN SettledInvoiceJpaDto i ON p.hash = i.hash " + + "WHERE i.receivedVia = ?1 " + + "ORDER BY i.settleDate ASC") + List getSelfPaymentsToChannel(long channelId); + + @Query("SELECT NEW de.cotto.lndmanagej.selfpayments.persistence.SelfPaymentJpaDto(p, i) " + + "FROM PaymentJpaDto p " + + "JOIN SettledInvoiceJpaDto i ON p.hash = i.hash " + + "JOIN p.routes route " + + "JOIN route.hops hop " + + "WHERE hop.channelId = ?1 AND INDEX(hop) = 0 " + + "ORDER BY i.settleDate ASC") + List getSelfPaymentsFromChannel(long channelId); + + @Entity + @Table(name = "dummy") + class DummyEntity { + @Id + @SuppressWarnings("unused") + private long dummyId; + + public DummyEntity() { + // for JPA + } + } +} + diff --git a/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDtoTest.java b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDtoTest.java new file mode 100644 index 00000000..3c2ba89c --- /dev/null +++ b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentJpaDtoTest.java @@ -0,0 +1,37 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import de.cotto.lndmanagej.invoices.persistence.SettledInvoiceJpaDto; +import de.cotto.lndmanagej.payments.persistence.PaymentJpaDto; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; +import static org.assertj.core.api.Assertions.assertThat; + +class SelfPaymentJpaDtoTest { + private SelfPaymentJpaDto selfPaymentJpaDto; + + @BeforeEach + void setUp() { + SettledInvoiceJpaDto invoice = SettledInvoiceJpaDto.createFromModel(SETTLED_INVOICE); + PaymentJpaDto payment = PaymentJpaDto.createFromModel(PAYMENT); + selfPaymentJpaDto = new SelfPaymentJpaDto(payment, invoice); + } + + @Test + void toModel() { + assertThat(selfPaymentJpaDto.toModel()).isEqualTo(SELF_PAYMENT); + } + + @Test + void payment() { + assertThat(selfPaymentJpaDto.payment().toModel()).isEqualTo(PAYMENT); + } + + @Test + void settledInvoice() { + assertThat(selfPaymentJpaDto.settledInvoice().toModel()).isEqualTo(SETTLED_INVOICE); + } +} \ No newline at end of file diff --git a/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImplTest.java b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImplTest.java new file mode 100644 index 00000000..990f4fee --- /dev/null +++ b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsDaoImplTest.java @@ -0,0 +1,65 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import de.cotto.lndmanagej.invoices.persistence.SettledInvoiceJpaDto; +import de.cotto.lndmanagej.model.Payment; +import de.cotto.lndmanagej.model.SettledInvoice; +import de.cotto.lndmanagej.payments.persistence.PaymentJpaDto; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT; +import static de.cotto.lndmanagej.model.PaymentFixtures.PAYMENT_2; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE; +import static de.cotto.lndmanagej.model.SettledInvoiceFixtures.SETTLED_INVOICE_2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SelfPaymentsDaoImplTest { + @InjectMocks + private SelfPaymentsDaoImpl selfPaymentsDaoImpl; + + @Mock + private SelfPaymentsRepository repository; + + @Test + void getAllSelfPayments_empty() { + assertThat(selfPaymentsDaoImpl.getAllSelfPayments()).isEmpty(); + } + + @Test + void getAllSelfPayments() { + when(repository.getAllSelfPayments()) + .thenReturn(List.of(getDto(PAYMENT, SETTLED_INVOICE), getDto(PAYMENT_2, SETTLED_INVOICE_2))); + assertThat(selfPaymentsDaoImpl.getAllSelfPayments()).containsExactlyInAnyOrder(SELF_PAYMENT, SELF_PAYMENT_2); + } + + @Test + void getSelfPaymentsToChannel() { + when(repository.getSelfPaymentsToChannel(CHANNEL_ID.getShortChannelId())) + .thenReturn(List.of(getDto(PAYMENT, SETTLED_INVOICE))); + assertThat(selfPaymentsDaoImpl.getSelfPaymentsToChannel(CHANNEL_ID)).containsExactlyInAnyOrder(SELF_PAYMENT); + } + + @Test + void getSelfPaymentsFromChannel() { + when(repository.getSelfPaymentsFromChannel(CHANNEL_ID.getShortChannelId())) + .thenReturn(List.of(getDto(PAYMENT, SETTLED_INVOICE))); + assertThat(selfPaymentsDaoImpl.getSelfPaymentsFromChannel(CHANNEL_ID)).containsExactlyInAnyOrder(SELF_PAYMENT); + } + + private SelfPaymentJpaDto getDto(Payment payment, SettledInvoice settledInvoice) { + return new SelfPaymentJpaDto( + PaymentJpaDto.createFromModel(payment), + SettledInvoiceJpaDto.createFromModel(settledInvoice) + ); + } +} \ No newline at end of file diff --git a/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryTest.java b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryTest.java new file mode 100644 index 00000000..72243ca3 --- /dev/null +++ b/selfpayments/src/test/java/de/cotto/lndmanagej/selfpayments/persistence/SelfPaymentsRepositoryTest.java @@ -0,0 +1,13 @@ +package de.cotto.lndmanagej.selfpayments.persistence; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SelfPaymentsRepositoryTest { + @Test + void dummyEntity() { + // the class is required to define a repository + assertThat(new SelfPaymentsRepository.DummyEntity()).isNotNull(); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a99e0445..5dc5f26c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,6 +8,7 @@ include 'grpc-client' include 'invoices' include 'model' include 'payments' +include 'selfpayments' include 'statistics' include 'transactions' include 'web' diff --git a/web/src/integrationTest/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerIT.java b/web/src/integrationTest/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerIT.java new file mode 100644 index 00000000..2609e278 --- /dev/null +++ b/web/src/integrationTest/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerIT.java @@ -0,0 +1,67 @@ +package de.cotto.lndmanagej.controller; + +import de.cotto.lndmanagej.model.ChannelIdResolver; +import de.cotto.lndmanagej.service.SelfPaymentsService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID_4; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +@WebMvcTest(controllers = SelfPaymentsController.class) +class SelfPaymentsControllerIT { + private static final String CHANNEL_PREFIX = "/api/channel/" + CHANNEL_ID.getShortChannelId() + "/"; + + @Autowired + private MockMvc mockMvc; + + @MockBean + @SuppressWarnings("unused") + private ChannelIdResolver channelIdResolver; + + @MockBean + private SelfPaymentsService selfPaymentsService; + + @Test + void getSelfPaymentsFromChannel() throws Exception { + when(selfPaymentsService.getSelfPaymentsFromChannel(CHANNEL_ID)) + .thenReturn(List.of(SELF_PAYMENT_2, SELF_PAYMENT)); + mockMvc.perform(get(CHANNEL_PREFIX + "/self-payments-from-channel/")) + .andExpect(jsonPath("$[0].memo", is(SELF_PAYMENT_2.memo()))) + .andExpect(jsonPath("$[1].memo", is(SELF_PAYMENT.memo()))); + } + + @Test + void getSelfPaymentsToChannel() throws Exception { + when(selfPaymentsService.getSelfPaymentsToChannel(CHANNEL_ID)) + .thenReturn(List.of(SELF_PAYMENT, SELF_PAYMENT_2)); + mockMvc.perform(get(CHANNEL_PREFIX + "/self-payments-to-channel/")) + .andExpect(jsonPath("$[0].memo", is(SELF_PAYMENT.memo()))) + .andExpect(jsonPath("$[0].settleDate", is(SELF_PAYMENT.settleDate().toString()))) + .andExpect(jsonPath("$[0].value", is(String.valueOf(SELF_PAYMENT.value().milliSatoshis())))) + .andExpect(jsonPath("$[0].fees", is(String.valueOf(SELF_PAYMENT.fees().milliSatoshis())))) + .andExpect(jsonPath("$[0].firstChannel", is(CHANNEL_ID_4.toString()))) + .andExpect(jsonPath("$[0].lastChannel", is(CHANNEL_ID_2.toString()))) + .andExpect(jsonPath("$[1].memo", is(SELF_PAYMENT_2.memo()))) + .andExpect(jsonPath("$[1].settleDate", is(SELF_PAYMENT_2.settleDate().toString()))) + .andExpect(jsonPath("$[1].value", is(String.valueOf(SELF_PAYMENT_2.value().milliSatoshis())))) + .andExpect(jsonPath("$[1].fees", is(String.valueOf(SELF_PAYMENT_2.fees().milliSatoshis())))) + .andExpect(jsonPath("$[1].firstChannel", is(not(empty())))) + .andExpect(jsonPath("$[1].lastChannel", is(CHANNEL_ID.toString()))); + } + +} \ No newline at end of file diff --git a/web/src/main/java/de/cotto/lndmanagej/controller/SelfPaymentsController.java b/web/src/main/java/de/cotto/lndmanagej/controller/SelfPaymentsController.java new file mode 100644 index 00000000..d539e1e6 --- /dev/null +++ b/web/src/main/java/de/cotto/lndmanagej/controller/SelfPaymentsController.java @@ -0,0 +1,41 @@ +package de.cotto.lndmanagej.controller; + +import com.codahale.metrics.annotation.Timed; +import de.cotto.lndmanagej.controller.dto.ObjectMapperConfiguration; +import de.cotto.lndmanagej.controller.dto.SelfPaymentDto; +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.service.SelfPaymentsService; +import org.springframework.context.annotation.Import; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/api/") +@Import(ObjectMapperConfiguration.class) +public class SelfPaymentsController { + private final SelfPaymentsService selfPaymentsService; + + public SelfPaymentsController(SelfPaymentsService selfPaymentsService) { + this.selfPaymentsService = selfPaymentsService; + } + + @Timed + @GetMapping("/channel/{channelId}/self-payments-to-channel") + public List getSelfPaymentsToChannel(@PathVariable ChannelId channelId) { + return selfPaymentsService.getSelfPaymentsToChannel(channelId).stream() + .map(SelfPaymentDto::createFromModel) + .toList(); + } + + @Timed + @GetMapping("/channel/{channelId}/self-payments-from-channel") + public List getSelfPaymentsFromChannel(@PathVariable ChannelId channelId) { + return selfPaymentsService.getSelfPaymentsFromChannel(channelId).stream() + .map(SelfPaymentDto::createFromModel) + .toList(); + } +} diff --git a/web/src/main/java/de/cotto/lndmanagej/controller/dto/ObjectMapperConfiguration.java b/web/src/main/java/de/cotto/lndmanagej/controller/dto/ObjectMapperConfiguration.java index 799e08a5..3aeac5c5 100644 --- a/web/src/main/java/de/cotto/lndmanagej/controller/dto/ObjectMapperConfiguration.java +++ b/web/src/main/java/de/cotto/lndmanagej/controller/dto/ObjectMapperConfiguration.java @@ -10,6 +10,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import java.time.ZonedDateTime; + @Configuration public class ObjectMapperConfiguration { public ObjectMapperConfiguration() { @@ -23,6 +25,7 @@ public class ObjectMapperConfiguration { module.addSerializer(Pubkey.class, new ToStringSerializer()); module.addSerializer(ChannelId.class, new ToStringSerializer()); module.addSerializer(ChannelPoint.class, new ToStringSerializer()); + module.addSerializer(ZonedDateTime.class, new ToStringSerializer()); return new ObjectMapper().registerModule(module); } } diff --git a/web/src/main/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDto.java b/web/src/main/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDto.java new file mode 100644 index 00000000..046a32c5 --- /dev/null +++ b/web/src/main/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDto.java @@ -0,0 +1,26 @@ +package de.cotto.lndmanagej.controller.dto; + +import de.cotto.lndmanagej.model.ChannelId; +import de.cotto.lndmanagej.model.SelfPayment; + +import java.time.ZonedDateTime; + +public record SelfPaymentDto( + ZonedDateTime settleDate, + String memo, + String value, + String fees, + ChannelId firstChannel, + ChannelId lastChannel +) { + public static SelfPaymentDto createFromModel(SelfPayment selfPayment) { + return new SelfPaymentDto( + selfPayment.settleDate(), + selfPayment.memo(), + String.valueOf(selfPayment.value().milliSatoshis()), + String.valueOf(selfPayment.fees().milliSatoshis()), + selfPayment.firstChannel().orElseThrow(), + selfPayment.lastChannel().orElseThrow() + ); + } +} diff --git a/web/src/test/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerTest.java b/web/src/test/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerTest.java new file mode 100644 index 00000000..64f5e1a4 --- /dev/null +++ b/web/src/test/java/de/cotto/lndmanagej/controller/SelfPaymentsControllerTest.java @@ -0,0 +1,44 @@ +package de.cotto.lndmanagej.controller; + +import de.cotto.lndmanagej.controller.dto.SelfPaymentDto; +import de.cotto.lndmanagej.service.SelfPaymentsService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT_2; +import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class SelfPaymentsControllerTest { + @InjectMocks + private SelfPaymentsController selfPaymentsController; + + @Mock + private SelfPaymentsService service; + + @Test + void getSelfPaymentsToChannel() { + when(service.getSelfPaymentsToChannel(CHANNEL_ID)).thenReturn(List.of(SELF_PAYMENT, SELF_PAYMENT_2)); + assertThat(selfPaymentsController.getSelfPaymentsToChannel(CHANNEL_ID)).containsExactly( + SelfPaymentDto.createFromModel(SELF_PAYMENT), + SelfPaymentDto.createFromModel(SELF_PAYMENT_2) + ); + } + + @Test + void getSelfPaymentsFromChannel() { + when(service.getSelfPaymentsFromChannel(CHANNEL_ID)).thenReturn(List.of(SELF_PAYMENT, SELF_PAYMENT_2)); + assertThat(selfPaymentsController.getSelfPaymentsFromChannel(CHANNEL_ID)).containsExactly( + SelfPaymentDto.createFromModel(SELF_PAYMENT), + SelfPaymentDto.createFromModel(SELF_PAYMENT_2) + ); + } +} \ No newline at end of file diff --git a/web/src/test/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDtoTest.java b/web/src/test/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDtoTest.java new file mode 100644 index 00000000..c11fea0e --- /dev/null +++ b/web/src/test/java/de/cotto/lndmanagej/controller/dto/SelfPaymentDtoTest.java @@ -0,0 +1,41 @@ +package de.cotto.lndmanagej.controller.dto; + +import org.junit.jupiter.api.Test; + +import static de.cotto.lndmanagej.SelfPaymentFixtures.SELF_PAYMENT; +import static org.assertj.core.api.Assertions.assertThat; + +class SelfPaymentDtoTest { + + private final SelfPaymentDto dto = SelfPaymentDto.createFromModel(SELF_PAYMENT); + + @Test + void settleDate() { + assertThat(dto.settleDate()).isEqualTo(SELF_PAYMENT.settleDate()); + } + + @Test + void memo() { + assertThat(dto.memo()).isEqualTo(SELF_PAYMENT.memo()); + } + + @Test + void value() { + assertThat(dto.value()).isEqualTo(String.valueOf(SELF_PAYMENT.value().milliSatoshis())); + } + + @Test + void fees() { + assertThat(dto.fees()).isEqualTo(String.valueOf(SELF_PAYMENT.fees().milliSatoshis())); + } + + @Test + void firstChannel() { + assertThat(dto.firstChannel()).isEqualTo(SELF_PAYMENT.firstChannel().orElseThrow()); + } + + @Test + void lastChannel() { + assertThat(dto.lastChannel()).isEqualTo(SELF_PAYMENT.lastChannel().orElseThrow()); + } +} \ No newline at end of file