From ae91185e9e10e6c3230ffc41166db047f6c20101 Mon Sep 17 00:00:00 2001 From: Sergio Siccha Date: Fri, 7 Jun 2024 18:30:44 -0500 Subject: [PATCH 1/2] Initial Commit -> Spring Reactive Web + Redis Cache + Kafka --- .gitignore | 10 ++ docker-compose.yml | 7 ++ microservices/.gitignore | 38 ++++++++ microservices/fraud-evaluation/.gitignore | 38 ++++++++ microservices/fraud-evaluation/pom.xml | 51 +++++++++++ .../FraudEvaluationApplication.java | 13 +++ .../fraudevaluation/config/KafkaConfig.java | 52 +++++++++++ .../fraudevaluation/error/ErrorResponse.java | 13 +++ .../error/GlobalExceptionHandler.java | 23 +++++ .../kafka/consumer/FraudMessageConsumer.java | 9 ++ .../impl/FraudMessageConsumerImpl.java | 39 ++++++++ .../kafka/producer/FraudMessageProducer.java | 9 ++ .../impl/FraudMessageProducerImpl.java | 35 +++++++ .../model/constant/TransactionStatusEnum.java | 18 ++++ .../model/dto/TransactionDTO.java | 22 +++++ .../service/FraudEvaluationService.java | 10 ++ .../impl/FraudEvaluationServiceImpl.java | 40 ++++++++ .../src/main/resources/application.yml | 37 ++++++++ microservices/pom.xml | 24 +++++ microservices/transaction-gateway/.gitignore | 38 ++++++++ microservices/transaction-gateway/pom.xml | 58 ++++++++++++ .../src/main/resources/application.yml | 0 .../transaction-management/.gitignore | 38 ++++++++ .../transaction-management/README.md | 2 + microservices/transaction-management/pom.xml | 91 +++++++++++++++++++ .../TransactionManagementApplication.java | 13 +++ .../builder/TransactionBuilder.java | 52 +++++++++++ .../config/KafkaConfig.java | 52 +++++++++++ .../config/RedisConfig.java | 70 ++++++++++++++ .../error/ErrorResponse.java | 13 +++ .../error/GlobalExceptionHandler.java | 23 +++++ .../error/NotFoundException.java | 9 ++ .../consumer/TransactionMessageConsumer.java | 9 ++ .../impl/TransactionMessageConsumerImpl.java | 44 +++++++++ .../producer/TransactionMessageProducer.java | 9 ++ .../impl/TransactionMessageProducerImpl.java | 35 +++++++ .../model/common/TransactionState.java | 16 ++++ .../model/common/TransactionType.java | 16 ++++ .../model/constant/TransactionStatusEnum.java | 19 ++++ .../model/constant/TransactionTypeEnum.java | 26 ++++++ .../model/dto/TransactionDTO.java | 22 +++++ .../model/entity/TransactionEntity.java | 52 +++++++++++ .../request/RegisterTransactionRequest.java | 21 +++++ .../model/response/TransactionResponse.java | 25 +++++ .../repository/TransactionRepository.java | 29 ++++++ .../TransactionRepositoryConnector.java | 16 ++++ .../TransactionRepositoryConnectorImpl.java | 45 +++++++++ .../service/TransactionManagementService.java | 16 ++++ .../TransactionManagementServiceImpl.java | 58 ++++++++++++ .../transactionmanagement/util/Constants.java | 7 ++ .../transactionmanagement/util/Utils.java | 12 +++ .../web/TransactionManagementController.java | 44 +++++++++ .../src/main/resources/application.yml | 46 ++++++++++ script/01_init_query.sql | 30 ++++++ 54 files changed, 1544 insertions(+) create mode 100644 .gitignore create mode 100644 microservices/.gitignore create mode 100644 microservices/fraud-evaluation/.gitignore create mode 100644 microservices/fraud-evaluation/pom.xml create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/FraudEvaluationApplication.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/config/KafkaConfig.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/ErrorResponse.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/GlobalExceptionHandler.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/FraudMessageConsumer.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/impl/FraudMessageConsumerImpl.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/FraudMessageProducer.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/impl/FraudMessageProducerImpl.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/constant/TransactionStatusEnum.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/dto/TransactionDTO.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/FraudEvaluationService.java create mode 100644 microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java create mode 100644 microservices/fraud-evaluation/src/main/resources/application.yml create mode 100644 microservices/pom.xml create mode 100644 microservices/transaction-gateway/.gitignore create mode 100644 microservices/transaction-gateway/pom.xml create mode 100644 microservices/transaction-gateway/src/main/resources/application.yml create mode 100644 microservices/transaction-management/.gitignore create mode 100644 microservices/transaction-management/README.md create mode 100644 microservices/transaction-management/pom.xml create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/TransactionManagementApplication.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/builder/TransactionBuilder.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/KafkaConfig.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/RedisConfig.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/ErrorResponse.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/GlobalExceptionHandler.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/NotFoundException.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/TransactionMessageConsumer.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/impl/TransactionMessageConsumerImpl.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/TransactionMessageProducer.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/impl/TransactionMessageProducerImpl.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionState.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionType.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionStatusEnum.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionTypeEnum.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/dto/TransactionDTO.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/entity/TransactionEntity.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/response/TransactionResponse.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/TransactionRepository.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/TransactionRepositoryConnector.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/impl/TransactionRepositoryConnectorImpl.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/TransactionManagementService.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Constants.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Utils.java create mode 100644 microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java create mode 100644 microservices/transaction-management/src/main/resources/application.yml create mode 100644 script/01_init_query.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dccf9be --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml + +.idea/ diff --git a/docker-compose.yml b/docker-compose.yml index 6e9a9c5..939cc72 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,13 @@ services: environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres + redis: + image: "redis:latest" + ports: + - "6379:6379" + environment: + - ALLOW_EMPTY_PASSWORD=yes + - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL zookeeper: image: confluentinc/cp-zookeeper:5.5.3 environment: diff --git a/microservices/.gitignore b/microservices/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/microservices/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/microservices/fraud-evaluation/.gitignore b/microservices/fraud-evaluation/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/microservices/fraud-evaluation/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/microservices/fraud-evaluation/pom.xml b/microservices/fraud-evaluation/pom.xml new file mode 100644 index 0000000..bf2243e --- /dev/null +++ b/microservices/fraud-evaluation/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + fraud-evaluation + 1.0.0-SNAPSHOT + fraud-evaluation + Fraud Evaluation Service + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + + org.springframework.kafka + spring-kafka + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + \ No newline at end of file diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/FraudEvaluationApplication.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/FraudEvaluationApplication.java new file mode 100644 index 0000000..f5c1b1d --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/FraudEvaluationApplication.java @@ -0,0 +1,13 @@ +package com.yape.fraudevaluation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FraudEvaluationApplication { + + public static void main(String[] args) { + SpringApplication.run(FraudEvaluationApplication.class, args); + } + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/config/KafkaConfig.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/config/KafkaConfig.java new file mode 100644 index 0000000..0847746 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/config/KafkaConfig.java @@ -0,0 +1,52 @@ +package com.yape.fraudevaluation.config; + +import com.yape.fraudevaluation.model.dto.TransactionDTO; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.*; +import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String serverUrl; + + @Value("${spring.kafka.consumer.group-id}") + private String groupId; + + @Value("${spring.kafka.consumer.offset-mode}") + private String offsetMode; + + @Bean + public ConsumerFactory consumerFactory() { + Map configProps = new HashMap<>(); + + configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, serverUrl); + configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + configProps.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class); + configProps.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, StringDeserializer.class); + configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offsetMode); + + return new DefaultKafkaConsumerFactory<>(configProps, new ErrorHandlingDeserializer<>(), new ErrorHandlingDeserializer<>(new JsonDeserializer<>(TransactionDTO.class))); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + + return factory; + } + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/ErrorResponse.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/ErrorResponse.java new file mode 100644 index 0000000..11c08ca --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/ErrorResponse.java @@ -0,0 +1,13 @@ +package com.yape.fraudevaluation.error; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ErrorResponse { + + private Integer errorCode; + private String description; + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/GlobalExceptionHandler.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/GlobalExceptionHandler.java new file mode 100644 index 0000000..dcb1cf0 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/error/GlobalExceptionHandler.java @@ -0,0 +1,23 @@ +package com.yape.fraudevaluation.error; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Throwable.class) + public ResponseEntity handle(Throwable ex) { + log.error("Ocurrió un error en la aplicación {}", ex.toString()); + ex.printStackTrace(); + return new ResponseEntity<>(ErrorResponse.builder() + .errorCode(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .description(ex.getMessage()) + .build(), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/FraudMessageConsumer.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/FraudMessageConsumer.java new file mode 100644 index 0000000..1a7c539 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/FraudMessageConsumer.java @@ -0,0 +1,9 @@ +package com.yape.fraudevaluation.kafka.consumer; + +import com.yape.fraudevaluation.model.dto.TransactionDTO; + +public interface FraudMessageConsumer { + + void retrieveMessage(TransactionDTO object); + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/impl/FraudMessageConsumerImpl.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/impl/FraudMessageConsumerImpl.java new file mode 100644 index 0000000..045a239 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/consumer/impl/FraudMessageConsumerImpl.java @@ -0,0 +1,39 @@ +package com.yape.fraudevaluation.kafka.consumer.impl; + +import com.yape.fraudevaluation.kafka.consumer.FraudMessageConsumer; +import com.yape.fraudevaluation.model.dto.TransactionDTO; +import com.yape.fraudevaluation.service.FraudEvaluationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FraudMessageConsumerImpl implements FraudMessageConsumer { + + @Value("${fraud-evaluation.kafka-topics.transaction}") + private String transactionTopic; + + private final FraudEvaluationService fraudEvaluationService; + + @KafkaListener( + topics = "${fraud-evaluation.kafka-topics.transaction}", + groupId = "${spring.kafka.consumer.group-id}" + ) + @Override + public void retrieveMessage(TransactionDTO object) { + try { + log.info("Retreived message from Topic {}. Message {}", transactionTopic, object.toString()); + + fraudEvaluationService.evaluateTransaction(object) + .subscribe(); + } catch (Exception e) { + log.error("Exception on message send {}", e.toString()); + throw new RuntimeException("Error occurs message retrieve"); + } + } + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/FraudMessageProducer.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/FraudMessageProducer.java new file mode 100644 index 0000000..721984a --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/FraudMessageProducer.java @@ -0,0 +1,9 @@ +package com.yape.fraudevaluation.kafka.producer; + +import com.yape.fraudevaluation.model.dto.TransactionDTO; + +public interface FraudMessageProducer { + + boolean sendMessage(TransactionDTO transactionDTO); + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/impl/FraudMessageProducerImpl.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/impl/FraudMessageProducerImpl.java new file mode 100644 index 0000000..4ba7aa1 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/kafka/producer/impl/FraudMessageProducerImpl.java @@ -0,0 +1,35 @@ +package com.yape.fraudevaluation.kafka.producer.impl; + +import com.yape.fraudevaluation.kafka.producer.FraudMessageProducer; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.yape.fraudevaluation.model.dto.TransactionDTO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FraudMessageProducerImpl implements FraudMessageProducer { + + private final KafkaTemplate kafkaTemplate; + + @Value("${fraud-evaluation.kafka-topics.fraud-evaluation}") + private String fraudTopic; + + @Override + public boolean sendMessage(TransactionDTO transactionDTO) { + try { + log.info("Sending message {} to Topic {}", transactionDTO.toString(), fraudTopic); + + kafkaTemplate.send(fraudTopic, transactionDTO); + + return true; + } catch (Exception e) { + log.error("Exception on message send {}", e.toString()); + throw new RuntimeException("Error occurs on message send"); + } + } + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/constant/TransactionStatusEnum.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/constant/TransactionStatusEnum.java new file mode 100644 index 0000000..6a3305c --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/constant/TransactionStatusEnum.java @@ -0,0 +1,18 @@ +package com.yape.fraudevaluation.model.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TransactionStatusEnum { + + PENDING(1, "PENDING"), + APPROBED(2, "APPROBED"), + REJECTED(3, "REJECTED") + ; + + private final Integer value; + private final String description; + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/dto/TransactionDTO.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/dto/TransactionDTO.java new file mode 100644 index 0000000..417bb43 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/model/dto/TransactionDTO.java @@ -0,0 +1,22 @@ +package com.yape.fraudevaluation.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionDTO implements Serializable { + + private Long transactionId; + private String transactionCode; + private String transactionState; + private BigDecimal transactionValue; + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/FraudEvaluationService.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/FraudEvaluationService.java new file mode 100644 index 0000000..1d9b5ee --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/FraudEvaluationService.java @@ -0,0 +1,10 @@ +package com.yape.fraudevaluation.service; + +import com.yape.fraudevaluation.model.dto.TransactionDTO; +import reactor.core.publisher.Mono; + +public interface FraudEvaluationService { + + Mono evaluateTransaction(TransactionDTO transactionDTO); + +} diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java new file mode 100644 index 0000000..b452c2b --- /dev/null +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java @@ -0,0 +1,40 @@ +package com.yape.fraudevaluation.service.impl; + +import com.yape.fraudevaluation.kafka.producer.FraudMessageProducer; +import com.yape.fraudevaluation.model.constant.TransactionStatusEnum; +import com.yape.fraudevaluation.model.dto.TransactionDTO; +import com.yape.fraudevaluation.service.FraudEvaluationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +import java.math.BigDecimal; + +@Slf4j +@Service +@RequiredArgsConstructor +public class FraudEvaluationServiceImpl implements FraudEvaluationService { + + @Value("${fraud-evaluation.value-limit}") + private BigDecimal valueLimit; + + private final FraudMessageProducer fraudMessageProducer; + + @Override + public Mono evaluateTransaction(TransactionDTO transactionDTO) { + return Mono.just(transactionDTO) + .filter(transaction -> TransactionStatusEnum.PENDING.getDescription().equals(transactionDTO.getTransactionState())) + .doOnNext(this::evaluateValue) + .flatMap(transaction -> Mono.fromCallable(() -> fraudMessageProducer.sendMessage(transaction)) + .then()); + } + + private void evaluateValue(TransactionDTO transaction) { + transaction.setTransactionState( (transaction.getTransactionValue().compareTo(valueLimit) > 0) + ? TransactionStatusEnum.REJECTED.getDescription() + : TransactionStatusEnum.APPROBED.getDescription()); + } + +} diff --git a/microservices/fraud-evaluation/src/main/resources/application.yml b/microservices/fraud-evaluation/src/main/resources/application.yml new file mode 100644 index 0000000..7f8f907 --- /dev/null +++ b/microservices/fraud-evaluation/src/main/resources/application.yml @@ -0,0 +1,37 @@ +application: + core: + api: + path: /api/v1.0/fraud-evaluation + +server: + port: 8094 + address: + compression: + mime-types: + +spring: + kafka: + bootstrap-servers: 'localhost:9092' + consumer: + group-id: yape-group + offset-mode: 'earliest' + properties: + spring: + json: + trusted: + packages: com.yape.transactionmanagement.model.dto + producer: + properties: + spring: + json: + add: + type: + headers: false + key-serializer: org.springframework.kafka.support.serializer.JsonSerializer + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer + +fraud-evaluation: + kafka-topics: + fraud-evaluation: 'topic-fraud' + transaction: 'topic-transaction' + value-limit: 1000 \ No newline at end of file diff --git a/microservices/pom.xml b/microservices/pom.xml new file mode 100644 index 0000000..738a515 --- /dev/null +++ b/microservices/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.0 + + + + com.yape + microservices + 1.0.0-SNAPSHOT + pom + + + fraud-evaluation + transaction-management + transaction-gateway + + + \ No newline at end of file diff --git a/microservices/transaction-gateway/.gitignore b/microservices/transaction-gateway/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/microservices/transaction-gateway/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/microservices/transaction-gateway/pom.xml b/microservices/transaction-gateway/pom.xml new file mode 100644 index 0000000..90a4334 --- /dev/null +++ b/microservices/transaction-gateway/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + transaction-gateway + 1.0.0-SNAPSHOT + transaction-gateway + Transaction Management Gateway + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + \ No newline at end of file diff --git a/microservices/transaction-gateway/src/main/resources/application.yml b/microservices/transaction-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..e69de29 diff --git a/microservices/transaction-management/.gitignore b/microservices/transaction-management/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/microservices/transaction-management/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/microservices/transaction-management/README.md b/microservices/transaction-management/README.md new file mode 100644 index 0000000..e420bdf --- /dev/null +++ b/microservices/transaction-management/README.md @@ -0,0 +1,2 @@ +1.- Crear BD bd_challenge +2.- \ No newline at end of file diff --git a/microservices/transaction-management/pom.xml b/microservices/transaction-management/pom.xml new file mode 100644 index 0000000..818e270 --- /dev/null +++ b/microservices/transaction-management/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + transaction-management + 1.0.0-SNAPSHOT + transaction-management + Transaction Management Service + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.postgresql + postgresql + runtime + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + redis.clients + jedis + + + + + + org.springframework.kafka + spring-kafka + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + \ No newline at end of file diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/TransactionManagementApplication.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/TransactionManagementApplication.java new file mode 100644 index 0000000..ca7b6a6 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/TransactionManagementApplication.java @@ -0,0 +1,13 @@ +package com.yape.transactionmanagement; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TransactionManagementApplication { + + public static void main(String[] args) { + SpringApplication.run(TransactionManagementApplication.class, args); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/builder/TransactionBuilder.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/builder/TransactionBuilder.java new file mode 100644 index 0000000..a06eeb1 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/builder/TransactionBuilder.java @@ -0,0 +1,52 @@ +package com.yape.transactionmanagement.builder; + +import com.yape.transactionmanagement.model.common.TransactionState; +import com.yape.transactionmanagement.model.common.TransactionType; +import com.yape.transactionmanagement.model.constant.TransactionStatusEnum; +import com.yape.transactionmanagement.model.constant.TransactionTypeEnum; +import com.yape.transactionmanagement.model.dto.TransactionDTO; +import com.yape.transactionmanagement.model.request.RegisterTransactionRequest; +import com.yape.transactionmanagement.model.response.TransactionResponse; +import com.yape.transactionmanagement.model.entity.TransactionEntity; +import com.yape.transactionmanagement.util.Utils; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class TransactionBuilder { + + public static TransactionResponse buildTransactionResponse(TransactionEntity entity) { + return TransactionResponse.builder() + .transactionExternalId(entity.getTransactionCode()) + .transactionType(TransactionType.builder() + .name(entity.getTransactionType()).build()) + .transactionStatus(TransactionState.builder() + .name(entity.getTransactionStatus()).build()) + .value(entity.getTransactionValue()) + .createdAt(Utils.formatDateTime(entity.getCreatedAt())) + .build(); + } + + public static TransactionEntity buildTransactionEntity(RegisterTransactionRequest request) { + return TransactionEntity.builder() + .transactionCode(UUID.randomUUID().toString()) + .accountExternalIdDebit(request.getAccountExternalIdDebit()) + .accountExternalIdCredit(request.getAccountExternalIdCredit()) + .transactionType(TransactionTypeEnum.parse(request.getTranferTypeId()).getDescription()) + .transactionStatus(TransactionStatusEnum.PENDING.getDescription()) + .transactionValue(request.getValue()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static TransactionDTO buildTransactionDTO(TransactionEntity entity) { + return TransactionDTO.builder() + .transactionId(entity.getTransactionId()) + .transactionCode(entity.getTransactionCode()) + .transactionState(entity.getTransactionStatus()) + .transactionValue(entity.getTransactionValue()) + .build(); + } + + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/KafkaConfig.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/KafkaConfig.java new file mode 100644 index 0000000..35c774c --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/KafkaConfig.java @@ -0,0 +1,52 @@ +package com.yape.transactionmanagement.config; + +import com.yape.transactionmanagement.model.dto.TransactionDTO; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.*; +import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String serverUrl; + + @Value("${spring.kafka.consumer.group-id}") + private String groupId; + + @Value("${spring.kafka.consumer.offset-mode}") + private String offsetMode; + + @Bean + public ConsumerFactory consumerFactory() { + Map configProps = new HashMap<>(); + + configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, serverUrl); + configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + configProps.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class); + configProps.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, StringDeserializer.class); + configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, offsetMode); + + return new DefaultKafkaConsumerFactory<>(configProps, new ErrorHandlingDeserializer<>(), new ErrorHandlingDeserializer<>(new JsonDeserializer<>(TransactionDTO.class))); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + + return factory; + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/RedisConfig.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/RedisConfig.java new file mode 100644 index 0000000..a7dc864 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/config/RedisConfig.java @@ -0,0 +1,70 @@ +package com.yape.transactionmanagement.config; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import redis.clients.jedis.Jedis; + +import java.io.Serializable; +import java.time.Duration; + +@Slf4j +@Configuration +@AutoConfigureAfter(RedisAutoConfiguration.class) +@EnableCaching +public class RedisConfig { + + @Value("${redis.url}") + private String redisUrl; + + @Value("${redis.ttl-secconds}") + private long ttlSeconds; + + @Bean + public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + + return template; + } + + @Bean + public CacheManager cacheManager(RedisConnectionFactory factory) { + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); + + RedisCacheConfiguration redisCacheConfiguration = config + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair + .fromSerializer(new GenericJackson2JsonRedisSerializer())) + .entryTtl(Duration.ofSeconds(ttlSeconds)); + + return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration) + .build(); + } + + @PostConstruct + public void clearCache() { + log.info("Cleaning all cache entries. Test Only"); + Jedis jedis = new Jedis(redisUrl); + jedis.flushAll(); + jedis.close(); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/ErrorResponse.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/ErrorResponse.java new file mode 100644 index 0000000..dd29d9f --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/ErrorResponse.java @@ -0,0 +1,13 @@ +package com.yape.transactionmanagement.error; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ErrorResponse { + + private Integer errorCode; + private String description; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/GlobalExceptionHandler.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/GlobalExceptionHandler.java new file mode 100644 index 0000000..a837b67 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/GlobalExceptionHandler.java @@ -0,0 +1,23 @@ +package com.yape.transactionmanagement.error; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Throwable.class) + public ResponseEntity handle(Throwable ex) { + log.error("Ocurrió un error en la aplicación {}", ex.toString()); + ex.printStackTrace(); + return new ResponseEntity<>(ErrorResponse.builder() + .errorCode(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .description(ex.getMessage()) + .build(), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/NotFoundException.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/NotFoundException.java new file mode 100644 index 0000000..0d04388 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/error/NotFoundException.java @@ -0,0 +1,9 @@ +package com.yape.transactionmanagement.error; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + super(message); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/TransactionMessageConsumer.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/TransactionMessageConsumer.java new file mode 100644 index 0000000..109fe20 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/TransactionMessageConsumer.java @@ -0,0 +1,9 @@ +package com.yape.transactionmanagement.kafka.consumer; + +import com.yape.transactionmanagement.model.dto.TransactionDTO; + +public interface TransactionMessageConsumer { + + void retrieveMessage(TransactionDTO object); + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/impl/TransactionMessageConsumerImpl.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/impl/TransactionMessageConsumerImpl.java new file mode 100644 index 0000000..304aa49 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/consumer/impl/TransactionMessageConsumerImpl.java @@ -0,0 +1,44 @@ +package com.yape.transactionmanagement.kafka.consumer.impl; + +import com.yape.transactionmanagement.kafka.consumer.TransactionMessageConsumer; +import com.yape.transactionmanagement.model.dto.TransactionDTO; +import com.yape.transactionmanagement.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Caching; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TransactionMessageConsumerImpl implements TransactionMessageConsumer { + + @Value("${transaction-management.kafka-topics.fraud-evaluation}") + private String fraudTopic; + + private final TransactionRepository transactionRepository; + + @Caching(evict = { + @CacheEvict(value = "transactions", allEntries = true), + @CacheEvict(value = "transaction", key = "#object.transactionId") + }) + @KafkaListener( + topics = "${transaction-management.kafka-topics.fraud-evaluation}", + groupId = "${spring.kafka.consumer.group-id}" + ) + @Override + public void retrieveMessage(TransactionDTO object) { + try { + log.info("Retreived message from Topic {}. Message {}", fraudTopic, object.toString()); + + transactionRepository.updateTransactionState(object.getTransactionId(), object.getTransactionState()); + } catch (Exception e) { + log.error("Exception on message send {}", e.toString()); + throw new RuntimeException("Error occurs message retrieve"); + } + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/TransactionMessageProducer.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/TransactionMessageProducer.java new file mode 100644 index 0000000..1611160 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/TransactionMessageProducer.java @@ -0,0 +1,9 @@ +package com.yape.transactionmanagement.kafka.producer; + +import com.yape.transactionmanagement.model.dto.TransactionDTO; + +public interface TransactionMessageProducer { + + boolean sendMessage(TransactionDTO transactionDTO); + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/impl/TransactionMessageProducerImpl.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/impl/TransactionMessageProducerImpl.java new file mode 100644 index 0000000..0e83cc7 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/kafka/producer/impl/TransactionMessageProducerImpl.java @@ -0,0 +1,35 @@ +package com.yape.transactionmanagement.kafka.producer.impl; + +import com.yape.transactionmanagement.kafka.producer.TransactionMessageProducer; +import com.yape.transactionmanagement.model.dto.TransactionDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TransactionMessageProducerImpl implements TransactionMessageProducer { + + private final KafkaTemplate kafkaTemplate; + + @Value("${transaction-management.kafka-topics.transaction}") + private String transactionTopic; + + @Override + public boolean sendMessage(TransactionDTO transactionDTO) { + try { + log.info("Sending message {} to Topic {}", transactionDTO.toString(), transactionTopic); + + kafkaTemplate.send(transactionTopic, transactionDTO); + + return true; + } catch (Exception e) { + log.error("Exception on message send {}", e.toString()); + throw new RuntimeException("Error occurs on message send"); + } + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionState.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionState.java new file mode 100644 index 0000000..141d2cb --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionState.java @@ -0,0 +1,16 @@ +package com.yape.transactionmanagement.model.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionState { + + private String name; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionType.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionType.java new file mode 100644 index 0000000..4f5a9ac --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/common/TransactionType.java @@ -0,0 +1,16 @@ +package com.yape.transactionmanagement.model.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionType { + + private String name; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionStatusEnum.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionStatusEnum.java new file mode 100644 index 0000000..bd6fb54 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionStatusEnum.java @@ -0,0 +1,19 @@ +package com.yape.transactionmanagement.model.constant; + +import com.yape.transactionmanagement.error.NotFoundException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TransactionStatusEnum { + + PENDING(1, "PENDING"), + APPROBED(2, "APPROBED"), + REJECTED(3, "REJECTED") + ; + + private final Integer value; + private final String description; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionTypeEnum.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionTypeEnum.java new file mode 100644 index 0000000..8beefe5 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/constant/TransactionTypeEnum.java @@ -0,0 +1,26 @@ +package com.yape.transactionmanagement.model.constant; + +import com.yape.transactionmanagement.error.NotFoundException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum TransactionTypeEnum { + + OWN(1, "OWN"), + THIRDPARTY(2, "THIRDPARTY"), + INTERBANK(3, "EXTERNAL") + ; + + private final Integer value; + private final String description; + + public static TransactionTypeEnum parse(Integer value) { + for (TransactionTypeEnum state : TransactionTypeEnum.values()) { + if (value.equals(state.getValue())) return state; + } + throw new NotFoundException("Transaction type not found."); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/dto/TransactionDTO.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/dto/TransactionDTO.java new file mode 100644 index 0000000..e7f74b3 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/dto/TransactionDTO.java @@ -0,0 +1,22 @@ +package com.yape.transactionmanagement.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionDTO implements Serializable { + + private Long transactionId; + private String transactionCode; + private String transactionState; + private BigDecimal transactionValue; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/entity/TransactionEntity.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/entity/TransactionEntity.java new file mode 100644 index 0000000..22d2242 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/entity/TransactionEntity.java @@ -0,0 +1,52 @@ +package com.yape.transactionmanagement.model.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity(name = "transaction") +@Table(name = "transaction", schema = "public") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionEntity { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long transactionId; + + @Column(name ="code") + private String transactionCode; + + @Column(name ="account_external_id_debit") + private String accountExternalIdDebit; + + @Column(name ="account_external_id_credit") + private String accountExternalIdCredit; + + @Column(name ="transfer_type") + private String transactionType; + + @Column(name ="value") + private BigDecimal transactionValue; + + @Column(name ="status") + private String transactionStatus; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Column(name = "created_at") + private LocalDateTime createdAt; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Column(name = "updated_at") + private LocalDateTime updatedAt; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java new file mode 100644 index 0000000..1d798af --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java @@ -0,0 +1,21 @@ +package com.yape.transactionmanagement.model.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class RegisterTransactionRequest { + + private String accountExternalIdDebit; + private String accountExternalIdCredit; + private Integer tranferTypeId; + private BigDecimal value; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/response/TransactionResponse.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/response/TransactionResponse.java new file mode 100644 index 0000000..f041478 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/response/TransactionResponse.java @@ -0,0 +1,25 @@ +package com.yape.transactionmanagement.model.response; + +import com.yape.transactionmanagement.model.common.TransactionState; +import com.yape.transactionmanagement.model.common.TransactionType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionResponse implements Serializable { + + private String transactionExternalId; + private TransactionType transactionType; + private TransactionState transactionStatus; + private BigDecimal value; + private String createdAt; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/TransactionRepository.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/TransactionRepository.java new file mode 100644 index 0000000..90f446c --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/TransactionRepository.java @@ -0,0 +1,29 @@ +package com.yape.transactionmanagement.repository; + +import com.yape.transactionmanagement.model.entity.TransactionEntity; +import jakarta.transaction.Transactional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface TransactionRepository extends JpaRepository { + + @Query(value = "SELECT * FROM public.find_all_transactions_sp()", nativeQuery = true) + List callGetTransactionsSP(); + + + @Query(value = "SELECT * FROM public.find_transaction_by_code_sp(:txCode)", nativeQuery = true) + TransactionEntity callGetTransactionByCode(@Param("txCode") String txCode); + + @Transactional + @Modifying + @Query(value = "UPDATE public.transaction SET status = :txState, updated_at = CURRENT_TIMESTAMP WHERE id = :txId", nativeQuery = true) + void updateTransactionState(@Param("txId") Long transactionId, + @Param("txState") String transactionState); + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/TransactionRepositoryConnector.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/TransactionRepositoryConnector.java new file mode 100644 index 0000000..4dca4c3 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/TransactionRepositoryConnector.java @@ -0,0 +1,16 @@ +package com.yape.transactionmanagement.repository.connector; + +import com.yape.transactionmanagement.model.entity.TransactionEntity; +import reactor.core.publisher.Mono; + +import java.util.List; + +public interface TransactionRepositoryConnector { + + Mono> findAllTransactions(); + + Mono findTransactionByCode(String transactionCode); + + Mono saveTransaction(TransactionEntity entity); + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/impl/TransactionRepositoryConnectorImpl.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/impl/TransactionRepositoryConnectorImpl.java new file mode 100644 index 0000000..bde4e40 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/repository/connector/impl/TransactionRepositoryConnectorImpl.java @@ -0,0 +1,45 @@ +package com.yape.transactionmanagement.repository.connector.impl; + +import com.yape.transactionmanagement.model.entity.TransactionEntity; +import com.yape.transactionmanagement.repository.TransactionRepository; +import com.yape.transactionmanagement.repository.connector.TransactionRepositoryConnector; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@Slf4j +@Component +@RequiredArgsConstructor +public class TransactionRepositoryConnectorImpl implements TransactionRepositoryConnector { + + private final TransactionRepository transactionRepository; + + @Override + public Mono> findAllTransactions() { + return Mono.fromFuture(CompletableFuture.supplyAsync(transactionRepository::callGetTransactionsSP)) + .doOnSubscribe(sub -> log.info("Buscando transacciones")) + .doOnSuccess(success -> log.info("Busqueda exitosa")) + .doOnError(err -> log.info("Ocurrio un error en la busqueda de todas las transacciones")); + } + + @Override + public Mono findTransactionByCode(String transactionCode) { + return Mono.fromFuture(CompletableFuture.supplyAsync(() -> transactionRepository.callGetTransactionByCode(transactionCode))) + .doOnSubscribe(sub -> log.info("Buscando transaccion {}", transactionCode)) + .doOnSuccess(success -> log.info("Busqueda exitosa")) + .doOnError(err -> log.info("Ocurrio un error en la busqueda de la transaccion")); + } + + @Override + public Mono saveTransaction(TransactionEntity entity) { + return Mono.fromFuture(CompletableFuture.supplyAsync(() -> transactionRepository.save(entity))) + .doOnSubscribe(sub -> log.info("Registrando nueva transaccion")) + .doOnSuccess(success -> log.info("Transaccion se registro correctamente")) + .doOnError(err -> log.info("Ocurrio un error al intentar registrar la transaccion")); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/TransactionManagementService.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/TransactionManagementService.java new file mode 100644 index 0000000..7476fed --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/TransactionManagementService.java @@ -0,0 +1,16 @@ +package com.yape.transactionmanagement.service; + +import com.yape.transactionmanagement.model.request.RegisterTransactionRequest; +import com.yape.transactionmanagement.model.response.TransactionResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface TransactionManagementService { + + Flux getAllTransactions(); + + Mono getTransactionByCode(String transactionCode); + + Mono registerTransaction(RegisterTransactionRequest request); + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java new file mode 100644 index 0000000..d521079 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java @@ -0,0 +1,58 @@ +package com.yape.transactionmanagement.service.impl; + +import com.yape.transactionmanagement.builder.TransactionBuilder; +import com.yape.transactionmanagement.kafka.producer.TransactionMessageProducer; +import com.yape.transactionmanagement.model.constant.TransactionStatusEnum; +import com.yape.transactionmanagement.model.request.RegisterTransactionRequest; +import com.yape.transactionmanagement.model.response.TransactionResponse; +import com.yape.transactionmanagement.repository.connector.TransactionRepositoryConnector; +import com.yape.transactionmanagement.service.TransactionManagementService; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TransactionManagementServiceImpl implements TransactionManagementService { + + private final TransactionRepositoryConnector transactionRepositoryConnector; + private final TransactionMessageProducer transactionMessageProducer; + + @Cacheable(cacheNames = "transactions") + @Override + public Flux getAllTransactions() { + return transactionRepositoryConnector.findAllTransactions() + .flatMapMany(Flux::fromIterable) + .map(TransactionBuilder::buildTransactionResponse); + } + + @Cacheable(cacheNames = "transaction", key = "#txCode", unless = "#result == null") + @Override + public Mono getTransactionByCode(String txCode) { + return transactionRepositoryConnector.findTransactionByCode(txCode) + .map(TransactionBuilder::buildTransactionResponse); + } + + @CacheEvict(cacheNames = "transactions", allEntries = true) + @Transactional + @Override + public Mono registerTransaction(RegisterTransactionRequest request) { + return Mono.fromCallable(() -> TransactionBuilder.buildTransactionEntity(request)) + .flatMap(entity -> transactionRepositoryConnector.saveTransaction(entity) + .flatMap(sEntity -> Mono.fromCallable(() -> transactionMessageProducer.sendMessage(TransactionBuilder.buildTransactionDTO(sEntity))) + .onErrorResume(err -> { + log.error("Updating state to REJECTED because can't evaluate transaction value. TxCode {}", sEntity.getTransactionCode()); + sEntity.setTransactionStatus(TransactionStatusEnum.REJECTED.getDescription()); + return transactionRepositoryConnector.saveTransaction(entity) + .thenReturn(Boolean.FALSE); + }))) + .then(); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Constants.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Constants.java new file mode 100644 index 0000000..43c39c7 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Constants.java @@ -0,0 +1,7 @@ +package com.yape.transactionmanagement.util; + +public class Constants { + + public static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Utils.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Utils.java new file mode 100644 index 0000000..4b88c8c --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/util/Utils.java @@ -0,0 +1,12 @@ +package com.yape.transactionmanagement.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Utils { + + public static String formatDateTime(LocalDateTime dateTime) { + return dateTime.format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)); + } + +} diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java new file mode 100644 index 0000000..7535d40 --- /dev/null +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java @@ -0,0 +1,44 @@ +package com.yape.transactionmanagement.web; + +import com.yape.transactionmanagement.model.request.RegisterTransactionRequest; +import com.yape.transactionmanagement.model.response.TransactionResponse; +import com.yape.transactionmanagement.service.TransactionManagementService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@RestController +@RequestMapping("${application.core.api.path}/transactions") +@RequiredArgsConstructor +@Validated +public class TransactionManagementController { + + private final TransactionManagementService transactionManagementService; + + @GetMapping(value = "", produces = MediaType.APPLICATION_NDJSON_VALUE) + public Flux getAllTransactions() { + return transactionManagementService.getAllTransactions() + .doOnSubscribe(sub -> log.info("Iniciando busqueda todas las transacciones")) + .doAfterTerminate(() -> log.info("Exito en la busqueda de todas las transacciones.")); + } + + @GetMapping(value = "/{transactionCode}", produces = MediaType.APPLICATION_NDJSON_VALUE) + public Mono getTransactionByCode(@PathVariable("transactionCode") String transactionCode) { + return transactionManagementService.getTransactionByCode(transactionCode) + .doOnSubscribe(sub -> log.info("Iniciando busqueda de transaccion por codigo {}", transactionCode)) + .doOnSuccess(response -> log.info("Exito en la busqueda de la transaccion.")); + } + + @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_NDJSON_VALUE) + public Mono registerTransaction(@RequestBody RegisterTransactionRequest request) { + return transactionManagementService.registerTransaction(request) + .doOnSubscribe(sub -> log.info("Iniciando registro de nueva transaccion")) + .doOnSuccess(response -> log.info("Exito en el registro de la transaccion.")); + } + +} diff --git a/microservices/transaction-management/src/main/resources/application.yml b/microservices/transaction-management/src/main/resources/application.yml new file mode 100644 index 0000000..dcef3a7 --- /dev/null +++ b/microservices/transaction-management/src/main/resources/application.yml @@ -0,0 +1,46 @@ +application: + core: + api: + path: /api/v1.0/transacion-management + +server: + port: 8093 + +spring: + datasource: + url: jdbc:postgresql://localhost:5432/bd_challenge + username: postgres + password: postgres + driver-class-name: org.postgresql.Driver + jpa: + hibernate: + ddl-auto: none + kafka: + bootstrap-servers: 'localhost:9092' + consumer: + group-id: yape-group + offset-mode: 'earliest' + properties: + spring: + json: + trusted: + packages: com.yape.fraudevaluation.model.dto + producer: + properties: + spring: + json: + add: + type: + headers: false + key-serializer: org.springframework.kafka.support.serializer.JsonSerializer + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer + +redis: + url: 'http://localhost:6379' + ttl-secconds: 60 + +transaction-management: + kafka-topics: + fraud-evaluation: 'topic-fraud' + transaction: 'topic-transaction' + diff --git a/script/01_init_query.sql b/script/01_init_query.sql new file mode 100644 index 0000000..06aa3e4 --- /dev/null +++ b/script/01_init_query.sql @@ -0,0 +1,30 @@ +CREATE TABLE public.transaction ( + "id" BIGSERIAL PRIMARY KEY, + "code" VARCHAR(100) NOT NULL, + "account_external_id_debit" VARCHAR(100) NOT NULL, + "account_external_id_credit" VARCHAR(100) NOT NULL, + "transfer_type" VARCHAR(50) NOT NULL, + "value" DECIMAL NOT NULL, + "status" VARCHAR(50) NOT NULL, + "created_at" TIMESTAMP, + "updated_at" TIMESTAMP, + CONSTRAINT "uq_code" UNIQUE ("code") +); + + + +CREATE OR REPLACE FUNCTION public.find_all_transactions_sp() RETURNS SETOF transaction AS $$ +BEGIN +RETURN QUERY SELECT * FROM public.transaction; +END; +$$ LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION public.find_transaction_by_code_sp(transaction_code VARCHAR) RETURNS transaction AS $$ +DECLARE +tx_result transaction%ROWTYPE; +BEGIN +SELECT * INTO tx_result FROM public.transaction WHERE "code" = transaction_code LIMIT 1; +RETURN tx_result; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file From 85ca8e141be11c4933b0b0ce4300347870e2e0d6 Mon Sep 17 00:00:00 2001 From: Sergio Siccha Date: Sat, 8 Jun 2024 16:44:38 -0500 Subject: [PATCH 2/2] Adding GraphQL querys and mutations on transaction-gateway --- microservices/README.md | 37 ++++++++ .../impl/FraudEvaluationServiceImpl.java | 2 +- microservices/transaction-gateway/pom.xml | 26 ++++-- .../TransactionGatewayApplication.java | 13 +++ .../config/GraphQLConfiguration.java | 17 ++++ .../config/client/TransactionRestClient.java | 16 ++++ .../impl/TransactionRestClientImpl.java | 89 +++++++++++++++++++ .../error/ErrorResponse.java | 13 +++ .../error/GlobalExceptionHandler.java | 27 ++++++ .../model/common/TransactionState.java | 16 ++++ .../model/common/TransactionType.java | 16 ++++ .../model/dto/RegisterTransactionRequest.java | 19 ++++ .../model/dto/TransactionResponse.java | 25 ++++++ .../service/TransactionService.java | 16 ++++ .../service/impl/TransactionServiceImpl.java | 41 +++++++++ .../web/TransactionGraphController.java | 40 +++++++++ .../src/main/resources/application.yml | 14 +++ .../main/resources/graphql/schema.graphqls | 35 ++++++++ .../transaction-management/README.md | 2 - microservices/transaction-management/pom.xml | 5 ++ .../request/RegisterTransactionRequest.java | 10 +++ .../TransactionManagementServiceImpl.java | 1 + .../web/TransactionManagementController.java | 9 +- .../src/main/resources/application.yml | 2 +- 24 files changed, 477 insertions(+), 14 deletions(-) create mode 100644 microservices/README.md create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/TransactionGatewayApplication.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/GraphQLConfiguration.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/TransactionRestClient.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/impl/TransactionRestClientImpl.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/ErrorResponse.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/GlobalExceptionHandler.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionState.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionType.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/RegisterTransactionRequest.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/TransactionResponse.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/TransactionService.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/impl/TransactionServiceImpl.java create mode 100644 microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/web/TransactionGraphController.java create mode 100644 microservices/transaction-gateway/src/main/resources/graphql/schema.graphqls delete mode 100644 microservices/transaction-management/README.md diff --git a/microservices/README.md b/microservices/README.md new file mode 100644 index 0000000..2d761c3 --- /dev/null +++ b/microservices/README.md @@ -0,0 +1,37 @@ +## Composición del proyecto +El presente proyecto esta conformado por tres módulos + +``` +1. transaction-gateway +2. transaction-management +3. fraud-evaluation +``` + +## Detalle de Módulos + +* transaction-gateway: Es el orquestador de las funcionalidades hacia el cliente. Utiliza GraphQL +* transaction-management: Microservicio dedicado para el mantenimiento de las transacciones en la BD. +* fraud-evaluation: Microservicio dedicado a la evaluación de fraudes de las transacciones registradas. + +## Pasos a seguir para compilar la aplicación + +1. Ejecutar el comando `docker-compose up -d` en la carpeta raiz del proyecto +2. Crear base de datos **bd_challenge** +3. Ejecutar script **01_init_query.sql** de la carpeta _/script_ +4. Compilar cada uno de los módulos listados previamente + + + +## Detalle de Funcionalidades + +El microservicio transaction-gateway expone las siguientes funcionalidades: + +1. Query + - getAllTransactions(limit, offset) + - getTransactionByCode(transactionCode) + + +2. Mutation + - createTransaction(transactionBody) + + diff --git a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java index b452c2b..8893c1e 100644 --- a/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java +++ b/microservices/fraud-evaluation/src/main/java/com/yape/fraudevaluation/service/impl/FraudEvaluationServiceImpl.java @@ -32,7 +32,7 @@ public Mono evaluateTransaction(TransactionDTO transactionDTO) { } private void evaluateValue(TransactionDTO transaction) { - transaction.setTransactionState( (transaction.getTransactionValue().compareTo(valueLimit) > 0) + transaction.setTransactionState((transaction.getTransactionValue().compareTo(valueLimit) > 0) ? TransactionStatusEnum.REJECTED.getDescription() : TransactionStatusEnum.APPROBED.getDescription()); } diff --git a/microservices/transaction-gateway/pom.xml b/microservices/transaction-gateway/pom.xml index 90a4334..3a508e2 100644 --- a/microservices/transaction-gateway/pom.xml +++ b/microservices/transaction-gateway/pom.xml @@ -11,11 +11,7 @@ transaction-gateway 1.0.0-SNAPSHOT transaction-gateway - Transaction Management Gateway - - - 1.8 - + Transaction Gateway Service @@ -36,6 +32,26 @@ true + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + org.springframework.boot + spring-boot-starter-graphql + + + + com.graphql-java + graphql-java-extended-scalars + 22.0 + + + diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/TransactionGatewayApplication.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/TransactionGatewayApplication.java new file mode 100644 index 0000000..18f07d1 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/TransactionGatewayApplication.java @@ -0,0 +1,13 @@ +package com.yape.transactiongateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TransactionGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(TransactionGatewayApplication.class, args); + } + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/GraphQLConfiguration.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/GraphQLConfiguration.java new file mode 100644 index 0000000..abbb652 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/GraphQLConfiguration.java @@ -0,0 +1,17 @@ +package com.yape.transactiongateway.config; + +import graphql.scalars.ExtendedScalars; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.graphql.execution.RuntimeWiringConfigurer; + +@Configuration +public class GraphQLConfiguration { + + @Bean + public RuntimeWiringConfigurer runtimeWiringConfigurer() { + return wiringBuilder -> wiringBuilder + .scalar(ExtendedScalars.GraphQLBigDecimal); + } + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/TransactionRestClient.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/TransactionRestClient.java new file mode 100644 index 0000000..8811c6c --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/TransactionRestClient.java @@ -0,0 +1,16 @@ +package com.yape.transactiongateway.config.client; + +import com.yape.transactiongateway.model.dto.RegisterTransactionRequest; +import com.yape.transactiongateway.model.dto.TransactionResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface TransactionRestClient { + + Flux getAllTransactions(); + + Mono getTransactionByCode(String transactionCode); + + Mono registerTransaction(RegisterTransactionRequest request); + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/impl/TransactionRestClientImpl.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/impl/TransactionRestClientImpl.java new file mode 100644 index 0000000..a9644cc --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/config/client/impl/TransactionRestClientImpl.java @@ -0,0 +1,89 @@ +package com.yape.transactiongateway.config.client.impl; + +import com.yape.transactiongateway.config.client.TransactionRestClient; +import com.yape.transactiongateway.model.dto.RegisterTransactionRequest; +import com.yape.transactiongateway.model.dto.TransactionResponse; +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class TransactionRestClientImpl implements TransactionRestClient { + + private WebClient restClient; + + @Value("${http-client.transaction-management.base-url}") + private String baseUrl; + + @Value("${http-client.transaction-management.read-timeout}") + private long readTimeout; + + @Value("${http-client.transaction-management.write-timeout}") + private long writeTimeout; + + @PostConstruct + public void init() { + HttpClient httpClient = HttpClient.create() + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ((Long) writeTimeout).intValue()) + .responseTimeout(Duration.ofMillis(readTimeout)) + .doOnConnected(conn -> + conn.addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.MILLISECONDS)) + .addHandlerLast(new WriteTimeoutHandler(writeTimeout, TimeUnit.MILLISECONDS))); + + this.restClient = WebClient.builder() + .baseUrl(baseUrl) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .build(); + } + + @Override + public Flux getAllTransactions() { + log.info("Begin HTTP Call [GET] transactions"); + return restClient.get() + .uri("/transactions") + .retrieve() + .bodyToFlux(TransactionResponse.class) + .doAfterTerminate(() -> log.info("OK <-- Respuesta de Éxito en la petición HTTP")) + .doOnError(err -> log.error("Error <-- Ocurrió un error en la petición HTTP. Msg {}", err.toString())) + ; + } + + @Override + public Mono getTransactionByCode(String transactionCode) { + log.info("Begin HTTP Call [GET] transactions/{txCode}"); + return restClient.get() + .uri("/transactions/{transactionCode}", transactionCode) + .retrieve() + .bodyToMono(TransactionResponse.class) + .doOnSuccess(success -> log.info("OK <-- Respuesta de Éxito en la petición HTTP")) + .doOnError(err -> log.error("Error <-- Ocurrió un error en la petición HTTP. Msg {}", err.toString())) + ; + } + + @Override + public Mono registerTransaction(RegisterTransactionRequest request) { + log.info("Begin HTTP Call [POST] transactions with request {}", request.toString()); + return restClient.post() + .uri("/transactions") + .bodyValue(request) + .retrieve() + .bodyToMono(Void.class) + .doOnSuccess(success -> log.info("OK <-- Respuesta de Éxito en la petición HTTP")) + .doOnError(err -> log.error("Error <-- Ocurrió un error en la petición HTTP. Msg {}", err.toString())) + ; + } + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/ErrorResponse.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/ErrorResponse.java new file mode 100644 index 0000000..43c4d3f --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/ErrorResponse.java @@ -0,0 +1,13 @@ +package com.yape.transactiongateway.error; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ErrorResponse { + + private Integer errorCode; + private String description; + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/GlobalExceptionHandler.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/GlobalExceptionHandler.java new file mode 100644 index 0000000..cc46f9c --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/error/GlobalExceptionHandler.java @@ -0,0 +1,27 @@ +package com.yape.transactiongateway.error; + +import graphql.GraphQLError; +import graphql.GraphqlErrorBuilder; +import graphql.schema.DataFetchingEnvironment; +import lombok.extern.slf4j.Slf4j; +import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter; +import org.springframework.graphql.execution.ErrorType; +import org.springframework.web.bind.annotation.ControllerAdvice; + +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler extends DataFetcherExceptionResolverAdapter { + + @Override + protected GraphQLError resolveToSingleError(Throwable ex, DataFetchingEnvironment env) { + log.error("Ocurrió un error en la aplicación {}", ex.toString()); + ex.printStackTrace(); + return GraphqlErrorBuilder.newError() + .errorType(ErrorType.INTERNAL_ERROR) + .message(ex.getMessage()) + .path(env.getExecutionStepInfo().getPath()) + .location(env.getField().getSourceLocation()) + .build(); + } + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionState.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionState.java new file mode 100644 index 0000000..7014386 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionState.java @@ -0,0 +1,16 @@ +package com.yape.transactiongateway.model.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionState { + + private String name; + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionType.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionType.java new file mode 100644 index 0000000..2808053 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/common/TransactionType.java @@ -0,0 +1,16 @@ +package com.yape.transactiongateway.model.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionType { + + private String name; + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/RegisterTransactionRequest.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/RegisterTransactionRequest.java new file mode 100644 index 0000000..b6ea96c --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/RegisterTransactionRequest.java @@ -0,0 +1,19 @@ +package com.yape.transactiongateway.model.dto; + +import lombok.*; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ToString +public class RegisterTransactionRequest { + + private String accountExternalIdDebit; + private String accountExternalIdCredit; + private Integer tranferTypeId; + private BigDecimal value; + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/TransactionResponse.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/TransactionResponse.java new file mode 100644 index 0000000..5d8a6f6 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/model/dto/TransactionResponse.java @@ -0,0 +1,25 @@ +package com.yape.transactiongateway.model.dto; + +import com.yape.transactiongateway.model.common.TransactionState; +import com.yape.transactiongateway.model.common.TransactionType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionResponse implements Serializable { + + private String transactionExternalId; + private TransactionType transactionType; + private TransactionState transactionStatus; + private BigDecimal value; + private String createdAt; + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/TransactionService.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/TransactionService.java new file mode 100644 index 0000000..8e708fa --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/TransactionService.java @@ -0,0 +1,16 @@ +package com.yape.transactiongateway.service; + +import com.yape.transactiongateway.model.dto.RegisterTransactionRequest; +import com.yape.transactiongateway.model.dto.TransactionResponse; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public interface TransactionService { + + Flux getAllTransactions(Integer limit, Integer offset); + + Mono getTransactionByCode(String transactionCode); + + Mono createTransaction(RegisterTransactionRequest transactionBody); + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/impl/TransactionServiceImpl.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/impl/TransactionServiceImpl.java new file mode 100644 index 0000000..29b562a --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/service/impl/TransactionServiceImpl.java @@ -0,0 +1,41 @@ +package com.yape.transactiongateway.service.impl; + +import com.yape.transactiongateway.config.client.TransactionRestClient; +import com.yape.transactiongateway.model.dto.RegisterTransactionRequest; +import com.yape.transactiongateway.model.dto.TransactionResponse; +import com.yape.transactiongateway.service.TransactionService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Objects; + +@Service +@RequiredArgsConstructor +public class TransactionServiceImpl implements TransactionService { + + private final TransactionRestClient transactionRestClient; + + @Override + public Flux getAllTransactions(Integer limit, Integer offset) { + Flux allTransactions = transactionRestClient.getAllTransactions(); + + if (Objects.nonNull(offset)) allTransactions = allTransactions.skip(offset); + + if (Objects.nonNull(limit)) allTransactions = allTransactions.take(limit); + + return allTransactions; + } + + @Override + public Mono getTransactionByCode(String transactionCode) { + return transactionRestClient.getTransactionByCode(transactionCode); + } + + @Override + public Mono createTransaction(RegisterTransactionRequest transactionBody) { + return transactionRestClient.registerTransaction(transactionBody); + } + +} diff --git a/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/web/TransactionGraphController.java b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/web/TransactionGraphController.java new file mode 100644 index 0000000..c08abf8 --- /dev/null +++ b/microservices/transaction-gateway/src/main/java/com/yape/transactiongateway/web/TransactionGraphController.java @@ -0,0 +1,40 @@ +package com.yape.transactiongateway.web; + +import com.yape.transactiongateway.model.dto.RegisterTransactionRequest; +import com.yape.transactiongateway.model.dto.TransactionResponse; +import com.yape.transactiongateway.service.TransactionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.MutationMapping; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.stereotype.Controller; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class TransactionGraphController { + + private final TransactionService transactionService; + + @QueryMapping + public Flux getAllTransactions(@Argument Integer limit, @Argument Integer offset) { + log.info("Iniciando consulta de transacciones. Limit: {} Offset {}", limit, offset); + return transactionService.getAllTransactions(limit, offset); + } + + @QueryMapping + public Mono getTransactionByCode(@Argument String transactionCode) { + log.info("Iniciando busqueda de la transaccion con codigo {}", transactionCode); + return transactionService.getTransactionByCode(transactionCode); + } + + @MutationMapping + public Mono createTransaction(@Argument RegisterTransactionRequest transactionBody) { + log.info("Creando una nueva transaccion"); + return transactionService.createTransaction(transactionBody); + } + +} diff --git a/microservices/transaction-gateway/src/main/resources/application.yml b/microservices/transaction-gateway/src/main/resources/application.yml index e69de29..30e3ef8 100644 --- a/microservices/transaction-gateway/src/main/resources/application.yml +++ b/microservices/transaction-gateway/src/main/resources/application.yml @@ -0,0 +1,14 @@ +spring: + graphql: + graphiql: + enabled: true + path: '/graphiql' + +server: + port: 8095 + +http-client: + transaction-management: + base-url: 'http://localhost:8093/api/v1.0/transaction-management' + read-timeout: 15000 + write-timeout: 15000 diff --git a/microservices/transaction-gateway/src/main/resources/graphql/schema.graphqls b/microservices/transaction-gateway/src/main/resources/graphql/schema.graphqls new file mode 100644 index 0000000..80d02f7 --- /dev/null +++ b/microservices/transaction-gateway/src/main/resources/graphql/schema.graphqls @@ -0,0 +1,35 @@ +scalar BigDecimal + +type TransactionType { + name: String +} + +type TransactionState { + name: String +} + +type Transaction { + transactionExternalId: String! + transactionType: TransactionType! + transactionStatus: TransactionState! + value: BigDecimal! + createdAt: String! +} + +input RegisterTransactionRequest { + accountExternalIdDebit: String! + accountExternalIdCredit: String! + tranferTypeId: Int! + value: BigDecimal! +} + + + +type Query { + getAllTransactions(limit: Int, offset: Int): [Transaction]! + getTransactionByCode(transactionCode: String): Transaction +} + +type Mutation { + createTransaction(transactionBody: RegisterTransactionRequest): Boolean +} \ No newline at end of file diff --git a/microservices/transaction-management/README.md b/microservices/transaction-management/README.md deleted file mode 100644 index e420bdf..0000000 --- a/microservices/transaction-management/README.md +++ /dev/null @@ -1,2 +0,0 @@ -1.- Crear BD bd_challenge -2.- \ No newline at end of file diff --git a/microservices/transaction-management/pom.xml b/microservices/transaction-management/pom.xml index 818e270..9f323a3 100644 --- a/microservices/transaction-management/pom.xml +++ b/microservices/transaction-management/pom.xml @@ -25,6 +25,11 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-validation + + org.postgresql postgresql diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java index 1d798af..555b208 100644 --- a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/model/request/RegisterTransactionRequest.java @@ -1,5 +1,8 @@ package com.yape.transactionmanagement.model.request; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,9 +16,16 @@ @Builder public class RegisterTransactionRequest { + @NotBlank(message = "El campo accountExternalIdDebit no puede enviarse vacio") private String accountExternalIdDebit; + + @NotBlank(message = "El campo accountExternalIdCredit no puede enviarse vacio") private String accountExternalIdCredit; + + @NotNull (message = "El campo tranferTypeId es obligatorio") private Integer tranferTypeId; + + @NotNull (message = "El campo tranferTypeId es obligatorio") private BigDecimal value; } diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java index d521079..3382159 100644 --- a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/service/impl/TransactionManagementServiceImpl.java @@ -29,6 +29,7 @@ public class TransactionManagementServiceImpl implements TransactionManagementSe public Flux getAllTransactions() { return transactionRepositoryConnector.findAllTransactions() .flatMapMany(Flux::fromIterable) + .onBackpressureBuffer() .map(TransactionBuilder::buildTransactionResponse); } diff --git a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java index 7535d40..1a4d063 100644 --- a/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java +++ b/microservices/transaction-management/src/main/java/com/yape/transactionmanagement/web/TransactionManagementController.java @@ -3,10 +3,10 @@ import com.yape.transactionmanagement.model.request.RegisterTransactionRequest; import com.yape.transactionmanagement.model.response.TransactionResponse; import com.yape.transactionmanagement.service.TransactionManagementService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -15,7 +15,6 @@ @RestController @RequestMapping("${application.core.api.path}/transactions") @RequiredArgsConstructor -@Validated public class TransactionManagementController { private final TransactionManagementService transactionManagementService; @@ -27,15 +26,15 @@ public Flux getAllTransactions() { .doAfterTerminate(() -> log.info("Exito en la busqueda de todas las transacciones.")); } - @GetMapping(value = "/{transactionCode}", produces = MediaType.APPLICATION_NDJSON_VALUE) + @GetMapping(value = "/{transactionCode}") public Mono getTransactionByCode(@PathVariable("transactionCode") String transactionCode) { return transactionManagementService.getTransactionByCode(transactionCode) .doOnSubscribe(sub -> log.info("Iniciando busqueda de transaccion por codigo {}", transactionCode)) .doOnSuccess(response -> log.info("Exito en la busqueda de la transaccion.")); } - @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_NDJSON_VALUE) - public Mono registerTransaction(@RequestBody RegisterTransactionRequest request) { + @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE) + public Mono registerTransaction(@Valid @RequestBody RegisterTransactionRequest request) { return transactionManagementService.registerTransaction(request) .doOnSubscribe(sub -> log.info("Iniciando registro de nueva transaccion")) .doOnSuccess(response -> log.info("Exito en el registro de la transaccion.")); diff --git a/microservices/transaction-management/src/main/resources/application.yml b/microservices/transaction-management/src/main/resources/application.yml index dcef3a7..9fe8130 100644 --- a/microservices/transaction-management/src/main/resources/application.yml +++ b/microservices/transaction-management/src/main/resources/application.yml @@ -1,7 +1,7 @@ application: core: api: - path: /api/v1.0/transacion-management + path: /api/v1.0/transaction-management server: port: 8093