diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/LoanRepaymentDueDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/LoanRepaymentDueDataV1.avsc index 87efc7499..add6a9056 100644 --- a/fineract-avro-schemas/src/main/avro/loan/v1/LoanRepaymentDueDataV1.avsc +++ b/fineract-avro-schemas/src/main/avro/loan/v1/LoanRepaymentDueDataV1.avsc @@ -38,6 +38,17 @@ "null", "org.apache.fineract.avro.loan.v1.RepaymentPastDueDataV1" ] + }, + { + "default": null, + "name": "customData", + "type": [ + "null", + { + "values": "bytes", + "type": "map" + } + ] } ] } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java index 84ad31b05..5050512d7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializer.java @@ -19,6 +19,7 @@ package org.apache.fineract.infrastructure.event.external.service.serialization.serializer.loan; import java.math.BigDecimal; +import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.avro.generic.GenericContainer; import org.apache.fineract.avro.generator.ByteBufferSerializable; @@ -30,7 +31,8 @@ import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentBusinessEvent; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanRepaymentPastDueDataMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroDateTimeMapper; -import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer; +import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.AbstractBusinessEventWithCustomDataSerializer; +import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.ExternalEventCustomDataSerializer; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentPastDueData; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -40,11 +42,12 @@ import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor -public class LoanRepaymentBusinessEventSerializer implements BusinessEventSerializer { +public class LoanRepaymentBusinessEventSerializer extends AbstractBusinessEventWithCustomDataSerializer { private final AvroDateTimeMapper dataTimeMapper; private final LoanRepaymentPastDueDataMapper pastDueDataMapper; private final LoanCalculateRepaymentPastDueService pastDueService; + private final List> externalEventCustomDataSerializers; @Override public ByteBufferSerializable toAvroDTO(BusinessEvent rawEvent) { @@ -68,6 +71,7 @@ public class LoanRepaymentBusinessEventSerializer implements BusinessEventSerial LoanRepaymentDueDataV1 loanRepaymentDueDataV1 = LoanRepaymentDueDataV1.newBuilder().setLoanId(id).setLoanAccountNo(accountNo) .setLoanExternalId(externalId).setCurrency(currency).setInstallment(repaymentDue).setPastDueAmount(pastDue).build(); + loanRepaymentDueDataV1.setCustomData(collectCustomData(event)); return loanRepaymentDueDataV1; } @@ -94,4 +98,9 @@ public class LoanRepaymentBusinessEventSerializer implements BusinessEventSerial public Class getSupportedSchema() { return LoanRepaymentDueDataV1.class; } + + @Override + protected List> getExternalEventCustomDataSerializers() { + return externalEventCustomDataSerializers; + } } diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java index 66af9aa8a..c6a82ef44 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanRepaymentBusinessEventSerializerTest.java @@ -18,7 +18,9 @@ */ package org.apache.fineract.infrastructure.event.external.service.serialization.serializer.loan; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -28,11 +30,13 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import java.nio.ByteBuffer; import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import org.apache.avro.AvroRuntimeException; import org.apache.fineract.avro.generic.v1.CurrencyDataV1; @@ -45,9 +49,11 @@ import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.repayment.LoanRepaymentDueBusinessEvent; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.loan.LoanRepaymentPastDueDataMapper; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroDateTimeMapper; +import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.ExternalEventCustomDataSerializer; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -80,6 +86,8 @@ public class LoanRepaymentBusinessEventSerializerTest { private MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); + private static final String CUSTOM_DATA_PREFIX = "test_data_loan_repayment_business_event"; + @BeforeEach public void setUp() { ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); @@ -100,7 +108,7 @@ public class LoanRepaymentBusinessEventSerializerTest { public void testLoanRepaymentEventPayloadSerialization() throws IOException { // given LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper, pastDueDataMapper, - pastDueService); + pastDueService, createCustomDataForEvents()); LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1); @@ -137,17 +145,21 @@ public class LoanRepaymentBusinessEventSerializerTest { RepaymentPastDueDataV1 pastDue = new RepaymentPastDueDataV1(BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0), BigDecimal.valueOf(0.0)); LoanRepaymentDueDataV1 expectedSerializedData = new LoanRepaymentDueDataV1(1L, "0001", "externalId", currency, repaymentDue, - pastDue); + pastDue, data.getCustomData()); assertEquals(data, expectedSerializedData); + assertNotNull(data.getCustomData()); + final Map customData = data.getCustomData(); + assertEquals(CUSTOM_DATA_PREFIX + "_1", new String(customData.get("test_key_1").array(), UTF_8)); + assertEquals(CUSTOM_DATA_PREFIX + "_2", new String(customData.get("test_key_2").array(), UTF_8)); } @Test public void testLoanRepaymentEventLoanIdMandatoryFieldValidation() { // given LoanRepaymentBusinessEventSerializer serializer = new LoanRepaymentBusinessEventSerializer(mapper, pastDueDataMapper, - pastDueService); + pastDueService, createCustomDataForEvents()); LocalDate loanInstallmentRepaymentDueDate = DateUtils.getBusinessLocalDate().plusDays(1); @@ -178,4 +190,41 @@ public class LoanRepaymentBusinessEventSerializerTest { AvroRuntimeException exceptionThrown = assertThrows(AvroRuntimeException.class, () -> serializer.toAvroDTO(event)); assertTrue(exceptionThrown.getMessage().contains("does not accept null values")); } + + private List> createCustomDataForEvents() { + return List.of(new ExternalEventCustomDataSerializer<>() { + + @Override + public ByteBuffer serialize(final LoanRepaymentBusinessEvent event) { + return ByteBuffer.wrap(CUSTOM_DATA_PREFIX.getBytes(UTF_8)); + } + + @Override + public String key() { + return "test_key_1"; + } + }, new ExternalEventCustomDataSerializer<>() { + + @Override + public ByteBuffer serialize(final LoanRepaymentBusinessEvent event) { + return ByteBuffer.wrap((CUSTOM_DATA_PREFIX + "_1").getBytes(UTF_8)); + } + + @Override + public String key() { + return "test_key_1"; + } + }, new ExternalEventCustomDataSerializer<>() { + + @Override + public ByteBuffer serialize(final LoanRepaymentBusinessEvent event) { + return ByteBuffer.wrap((CUSTOM_DATA_PREFIX + "_2").getBytes(UTF_8)); + } + + @Override + public String key() { + return "test_key_2"; + } + }); + } }