Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code challenge with kafka was implemented #16

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
126 changes: 126 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yape</groupId>
<artifactId>fraud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fraud</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.2.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.2.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>
14 changes: 14 additions & 0 deletions src/main/java/com/yape/fraud/FraudApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yape.fraud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
public class FraudApplication {

public static void main(String[] args) {
SpringApplication.run(FraudApplication.class, args);
}

}
39 changes: 39 additions & 0 deletions src/main/java/com/yape/fraud/controller/TransactionController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.yape.fraud.controller;

import com.yape.fraud.dto.TransactionDto;
import com.yape.fraud.dto.TransactionRequest;
import com.yape.fraud.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("v1/transaction")
public class TransactionController {

@Autowired
private TransactionService transactionService;

@PostMapping
public ResponseEntity<?> create(@RequestBody TransactionRequest transactionRequest) {
try {
TransactionDto result = transactionService.createTransaction(transactionRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
} catch (ResponseStatusException ex) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("mensaje", ex.getReason());
return ResponseEntity.status(ex.getStatusCode()).body(errorResponse);
}
}

@GetMapping("/{id}")
public ResponseEntity<?> getTransaction(@PathVariable Long id) {
TransactionDto result = transactionService.getTransaction(id);
return ResponseEntity.status(HttpStatus.OK).body(result);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/yape/fraud/dto/TransactionDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.yape.fraud.dto;

import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Getter
@Setter
public class TransactionDto {
private Long transactionExternalId;
private String accountExternalIdDebit;
private String accountExternalIdCredit;
private int tranferTypeId;
private double value;
private TransactionTypeDto transactionType;
private TransactionStatusDto transactionStatus;
private Date createdAt;
}
25 changes: 25 additions & 0 deletions src/main/java/com/yape/fraud/dto/TransactionMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.yape.fraud.dto;

import com.yape.fraud.model.Transaction;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper(componentModel = "spring")
public interface TransactionMapper {

@Mapping(target = "transactionExternalId", source = "id")
@Mapping(target = "transactionStatus.name", source = "transactionStatus")
@Mapping(target = "transactionType.name", expression = "java(mapTransactionType(transaction.getTranferTypeId()))")
TransactionDto toDto(Transaction transaction);

Transaction toModel(TransactionRequest request);

default String mapTransactionType(int tranferTypeId) {
switch (tranferTypeId) {
case 1: return "Transfer Type 1";
case 2: return "Transfer Type 2";
default: return "Unknown Type";
}
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/yape/fraud/dto/TransactionRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.yape.fraud.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TransactionRequest {
private String accountExternalIdDebit;
private String accountExternalIdCredit;
private int tranferTypeId;
private double value;
}
10 changes: 10 additions & 0 deletions src/main/java/com/yape/fraud/dto/TransactionStatusDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yape.fraud.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TransactionStatusDto {
private String name;
}
10 changes: 10 additions & 0 deletions src/main/java/com/yape/fraud/dto/TransactionTypeDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yape.fraud.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TransactionTypeDto {
private String name;
}
7 changes: 7 additions & 0 deletions src/main/java/com/yape/fraud/enums/TransactionStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yape.fraud.enums;

public enum TransactionStatus {
PENDING,
APPROVED,
REJECTED
}
33 changes: 33 additions & 0 deletions src/main/java/com/yape/fraud/model/Transaction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.yape.fraud.model;

import com.yape.fraud.enums.TransactionStatus;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Entity
@Getter
@Setter
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String accountExternalIdDebit;
private String accountExternalIdCredit;
private int tranferTypeId;
private double value;

@Enumerated(EnumType.STRING)
private TransactionStatus transactionStatus;

@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;

@PrePersist
protected void onCreate() {
this.createdAt = new Date();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yape.fraud.repository;

import com.yape.fraud.model.Transaction;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TransactionRepository extends JpaRepository<Transaction, Long> {
}
35 changes: 35 additions & 0 deletions src/main/java/com/yape/fraud/service/AntiFraudService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.yape.fraud.service;

import com.yape.fraud.enums.TransactionStatus;
import com.yape.fraud.model.Transaction;
import com.yape.fraud.repository.TransactionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class AntiFraudService {

@Autowired
private TransactionRepository transactionRepository;

@Autowired
private KafkaProducerService kafkaProducerService;

@KafkaListener(topics = "transaction-topic", groupId = "fraud-group")
public void validateTransaction(String transactionId) {
Long id = Long.parseLong(transactionId);
Transaction transaction = transactionRepository.findById(id).orElse(null);

if (transaction != null) {
if (transaction.getValue() > 1000) {
transaction.setTransactionStatus(TransactionStatus.REJECTED);
} else {
transaction.setTransactionStatus(TransactionStatus.APPROVED);
}
transactionRepository.save(transaction);
kafkaProducerService.sendTransactionUpdate(transactionId);
}
}
}

Loading