├── .coveralls.yml ├── lombok.config ├── spring-boot-logo.jpg ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── src ├── main │ ├── java │ │ └── com │ │ │ └── demo │ │ │ └── bankapp │ │ │ ├── request │ │ │ ├── BaseRequest.java │ │ │ ├── RetrieveWealthRequest.java │ │ │ ├── LoginRequest.java │ │ │ ├── FindAllTransactionsByUserRequest.java │ │ │ ├── CreateUserRequest.java │ │ │ ├── CreateTransactionRequest.java │ │ │ └── CreateTransferRequest.java │ │ │ ├── response │ │ │ ├── CreateUserResponse.java │ │ │ ├── RetrieveWealthResponse.java │ │ │ ├── CreateTransferResponse.java │ │ │ ├── FindAllUsersResponse.java │ │ │ ├── CreateTransactionResponse.java │ │ │ └── FindAllTransactionsByUserResponse.java │ │ │ ├── exception │ │ │ ├── TransactionLimitException.java │ │ │ ├── DailyOperationLimitReachedException.java │ │ │ ├── BadRequestException.java │ │ │ ├── BadCredentialsException.java │ │ │ ├── UserNotFoundException.java │ │ │ ├── InsufficientFundsException.java │ │ │ └── configuration │ │ │ │ ├── ApiError.java │ │ │ │ └── RestExceptionHandler.java │ │ │ ├── service │ │ │ ├── abstractions │ │ │ │ ├── ITransferService.java │ │ │ │ ├── IUserService.java │ │ │ │ ├── ITransactionService.java │ │ │ │ └── IWealthService.java │ │ │ └── concretions │ │ │ │ ├── TransferService.java │ │ │ │ ├── TransactionService.java │ │ │ │ ├── UserService.java │ │ │ │ └── WealthService.java │ │ │ ├── repository │ │ │ ├── WealthRepository.java │ │ │ ├── UserRepository.java │ │ │ ├── TransferRepository.java │ │ │ └── TransactionRepository.java │ │ │ ├── configuration │ │ │ ├── security │ │ │ │ ├── SecurityConstants.java │ │ │ │ ├── JWTAuthorizationFilter.java │ │ │ │ ├── SecurityConfig.java │ │ │ │ └── JWTAuthenticationFilter.java │ │ │ ├── Constants.java │ │ │ └── DatabaseMocker.java │ │ │ ├── model │ │ │ ├── User.java │ │ │ ├── Wealth.java │ │ │ ├── Transfer.java │ │ │ └── Transaction.java │ │ │ ├── BankApplication.java │ │ │ └── controller │ │ │ ├── WealthController.java │ │ │ ├── UserController.java │ │ │ ├── TransactionController.java │ │ │ └── TransferController.java │ └── resources │ │ └── application.properties └── test │ └── java │ └── com │ └── demo │ └── bankapp │ ├── BankApplicationTests.java │ ├── config │ └── TestUtils.java │ ├── repository │ ├── WealthRepositoryTest.java │ ├── TransferRepositoryTest.java │ ├── TransactionRepositoryTest.java │ └── UserRepositoryTest.java │ ├── service │ ├── TransferServiceTest.java │ ├── TransactionServiceTest.java │ ├── WealthServiceTest.java │ └── UserServiceTest.java │ └── controller │ ├── WealthControllerTest.java │ ├── UserControllerTest.java │ ├── TransactionControllerTest.java │ └── TransferControllerTest.java ├── .travis.yml ├── .gitignore ├── Dockerfile ├── pom.xml ├── README.md ├── mvnw.cmd └── mvnw /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true -------------------------------------------------------------------------------- /spring-boot-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mertakdut/Spring-Boot-Sample-Project/HEAD/spring-boot-logo.jpg -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mertakdut/Spring-Boot-Sample-Project/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/BaseRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | public class BaseRequest { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/CreateUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class CreateUserResponse { 7 | private String username; 8 | private String tcno; 9 | } 10 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar 3 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/RetrieveWealthResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import com.demo.bankapp.model.Wealth; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class RetrieveWealthResponse { 9 | private Wealth wealth; 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | before_install: 5 | - chmod +x mvnw 6 | install: 7 | - ./mvnw test-compile -DskipTests=true -Dmaven.javadoc.skip=true -B -V 8 | script: 9 | - ./mvnw test jacoco:report 10 | after_success: 11 | - ./mvnw coveralls:report -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/CreateTransferResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import com.demo.bankapp.model.Transfer; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class CreateTransferResponse { 9 | private Transfer transfer; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/FindAllUsersResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import java.util.List; 4 | 5 | import com.demo.bankapp.model.User; 6 | 7 | import lombok.Data; 8 | 9 | @Data 10 | public class FindAllUsersResponse { 11 | List userList; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/CreateTransactionResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import com.demo.bankapp.model.Transaction; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class CreateTransactionResponse { 9 | 10 | private Transaction transaction; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/RetrieveWealthRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @Data 7 | @EqualsAndHashCode(callSuper = false) 8 | public class RetrieveWealthRequest extends BaseRequest { 9 | 10 | private String username; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/LoginRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @Data 7 | @EqualsAndHashCode(callSuper = false) 8 | public class LoginRequest extends BaseRequest { 9 | 10 | private String username; 11 | private String password; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/FindAllTransactionsByUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @Data 7 | @EqualsAndHashCode(callSuper = false) 8 | public class FindAllTransactionsByUserRequest extends BaseRequest { 9 | 10 | private String username; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/response/FindAllTransactionsByUserResponse.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.response; 2 | 3 | import java.util.List; 4 | 5 | import com.demo.bankapp.model.Transaction; 6 | 7 | import lombok.Data; 8 | 9 | @Data 10 | public class FindAllTransactionsByUserResponse { 11 | private List transactionList; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/CreateUserRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | @Data 7 | @EqualsAndHashCode(callSuper = false) 8 | public class CreateUserRequest extends BaseRequest { 9 | 10 | private String username; 11 | private String password; 12 | private String tcno; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/TransactionLimitException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class TransactionLimitException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = -3442309139923977110L; 6 | 7 | public TransactionLimitException(String message) { 8 | super("Transaction Limit: " + message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/abstractions/ITransferService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.abstractions; 2 | 3 | import java.util.List; 4 | 5 | import com.demo.bankapp.model.Transfer; 6 | 7 | public interface ITransferService { 8 | 9 | Transfer createNewTransfer(Transfer transfer); 10 | 11 | List findAllTransfersFrom24Hours(Long userId); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/DailyOperationLimitReachedException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class DailyOperationLimitReachedException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = -6260854119635270900L; 6 | 7 | public DailyOperationLimitReachedException() { 8 | super("Daily transaction limit is reached."); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.servlet.context-path = /api 2 | spring.data.rest.base-path=/dev 3 | 4 | #Database 5 | #spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 6 | #spring.datasource.url=jdbc:mysql://localhost:3306/bankschema 7 | #spring.datasource.username=root 8 | #spring.datasource.password=mert123 9 | 10 | spring.jpa.properties.hibernate.id.new_generator_mappings=false 11 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/repository/WealthRepository.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | import com.demo.bankapp.model.Wealth; 7 | 8 | @RepositoryRestResource(exported = false) 9 | public interface WealthRepository extends JpaRepository { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/BankApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class BankApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/BadRequestException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class BadRequestException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 6338728573504497502L; 6 | 7 | public BadRequestException() { 8 | super("Request is malformed."); 9 | } 10 | 11 | public BadRequestException(String message) { 12 | super("Request is malformed: " + message); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/BadCredentialsException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class BadCredentialsException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = -349287396200850517L; 6 | 7 | public BadCredentialsException() { 8 | super("Bad Credentials."); 9 | } 10 | 11 | public BadCredentialsException(String message) { 12 | super("Bad Credentials: " + message); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class UserNotFoundException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = -1360953961105975949L; 6 | 7 | public UserNotFoundException() { 8 | super("User wealth not found"); 9 | } 10 | 11 | public UserNotFoundException(String username) { 12 | super("Could not find user " + username); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/CreateTransactionRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | @EqualsAndHashCode(callSuper = false) 10 | public class CreateTransactionRequest extends BaseRequest { 11 | 12 | private String username; 13 | private boolean isBuying; 14 | private String currency; 15 | private BigDecimal amount; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/request/CreateTransferRequest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.request; 2 | 3 | import java.math.BigDecimal; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | @EqualsAndHashCode(callSuper = false) 10 | public class CreateTransferRequest extends BaseRequest { 11 | 12 | private String senderUsername; 13 | private String receiverTcno; 14 | private String currency; 15 | private BigDecimal amount; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/security/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration.security; 2 | 3 | final class SecurityConstants { 4 | 5 | private SecurityConstants() { 6 | } 7 | 8 | public static final String SECRET = "SecretKeyToGenJWTs"; 9 | public static final long EXPIRATION_TIME = 864_000_000; // 10 days 10 | public static final String TOKEN_PREFIX = "Bearer "; 11 | public static final String HEADER_STRING = "Authorization"; 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.java.hsp 2 | *.sonarj 3 | *.sw* 4 | .DS_Store 5 | .settings 6 | .springBeans 7 | bin 8 | build.sh 9 | integration-repo 10 | ivy-cache 11 | jxl.log 12 | jmx.log 13 | derby.log 14 | spring-test/test-output/ 15 | .gradle 16 | argfile* 17 | activemq-data/ 18 | 19 | classes/ 20 | /build 21 | buildSrc/build 22 | /spring-*/build 23 | /src/asciidoc/build 24 | target/ 25 | 26 | # Eclipse artifacts, including WTP generated manifests 27 | .classpath 28 | .project 29 | spring-*/src/main/java/META-INF/MANIFEST.MF -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/abstractions/IUserService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.abstractions; 2 | 3 | import java.util.List; 4 | 5 | import com.demo.bankapp.model.User; 6 | 7 | public interface IUserService { 8 | 9 | List findAll(); 10 | 11 | User findByUserName(String username); 12 | 13 | User findByTcno(String tcno); 14 | 15 | User createNewUser(User user); 16 | 17 | boolean isUsernameExist(String username); 18 | 19 | boolean isTcnoExist(String tcno); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | import com.demo.bankapp.model.User; 7 | 8 | @RepositoryRestResource(exported = false) 9 | public interface UserRepository extends JpaRepository { 10 | 11 | User findByUsername(String username); 12 | 13 | User findByTcno(String tcno); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/InsufficientFundsException.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception; 2 | 3 | public class InsufficientFundsException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 8435355771655372975L; 6 | 7 | public InsufficientFundsException() { 8 | super("Insufficient Funds: Not enough TRY."); 9 | } 10 | 11 | public InsufficientFundsException(String currency) { 12 | super("Insufficient Funds: Your " + currency + " funds are not enough."); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/abstractions/ITransactionService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.abstractions; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.List; 5 | 6 | import com.demo.bankapp.model.Transaction; 7 | 8 | public interface ITransactionService { 9 | 10 | Transaction createNewTransaction(Long userId, boolean isBuying, String currency, BigDecimal amount); 11 | 12 | int getOperationCountFromLast24Hours(Long userId); 13 | 14 | List findAllByUserId(Long userId); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine as build 2 | WORKDIR /workspace/app 3 | 4 | COPY mvnw . 5 | COPY .mvn .mvn 6 | COPY pom.xml . 7 | COPY src src 8 | 9 | RUN ./mvnw package 10 | RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar) 11 | 12 | FROM openjdk:8-jdk-alpine 13 | VOLUME /tmp 14 | ARG DEPENDENCY=/workspace/app/target/dependency 15 | COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib 16 | COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF 17 | COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app 18 | ENTRYPOINT ["java","-cp","app:app/lib/*","com.demo.bankapp.BankApplication"] -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/model/User.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class User { 12 | 13 | private @Id @GeneratedValue Long id; 14 | private String username; 15 | private String password; 16 | private String tcno; 17 | 18 | private User() { 19 | } 20 | 21 | public User(String username, String password, String tcno) { 22 | this.username = username; 23 | this.password = password; 24 | this.tcno = tcno; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/abstractions/IWealthService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.abstractions; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Map; 5 | 6 | import com.demo.bankapp.model.Wealth; 7 | 8 | public interface IWealthService { 9 | 10 | void newWealthRecord(Long userId); 11 | 12 | Wealth findWealth(Long userId); 13 | 14 | Map getCurrencyRates(); 15 | 16 | void makeWealthExchange(Long userId, String currency, BigDecimal amount, boolean isBuying); 17 | 18 | void makeWealthTransaction(Long userId, String currency, BigDecimal amount, boolean isIncrementing); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/model/Wealth.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Map; 5 | 6 | import javax.persistence.ElementCollection; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | 11 | import lombok.Data; 12 | 13 | @Data 14 | @Entity 15 | public class Wealth { 16 | 17 | private @Id @GeneratedValue Long userId; 18 | 19 | @ElementCollection 20 | private Map wealthMap; 21 | 22 | private Wealth() { 23 | } 24 | 25 | public Wealth(Long userId, Map wealthMap) { 26 | this.userId = userId; 27 | this.wealthMap = wealthMap; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/BankApplication.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 7 | import org.springframework.security.crypto.password.PasswordEncoder; 8 | 9 | @SpringBootApplication 10 | public class BankApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(BankApplication.class, args); 14 | } 15 | 16 | @Bean 17 | public PasswordEncoder passwordEncoder() { 18 | return new BCryptPasswordEncoder(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/repository/TransferRepository.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 9 | 10 | import com.demo.bankapp.model.Transfer; 11 | 12 | @RepositoryRestResource(exported = false) 13 | public interface TransferRepository extends JpaRepository { 14 | 15 | @Query(value = "SELECT t FROM Transfer t WHERE t.fromUserId = :userId and t.transferTime >= DATEADD(day, -1, GETDATE())") 16 | List findAllTransfersFrom24Hours(@Param("userId") Long userId); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/configuration/ApiError.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception.configuration; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.springframework.http.HttpStatus; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class ApiError { 12 | 13 | private HttpStatus status; 14 | private String message; 15 | private List errors; 16 | 17 | public ApiError(final HttpStatus status, final String message, final List errors) { 18 | this.status = status; 19 | this.message = message; 20 | this.errors = errors; 21 | } 22 | 23 | public ApiError(final HttpStatus status, final String message, final String error) { 24 | super(); 25 | this.status = status; 26 | this.message = message; 27 | errors = Arrays.asList(error); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/model/Transfer.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | 10 | import lombok.Data; 11 | 12 | @Data 13 | @Entity 14 | public class Transfer { 15 | 16 | private @Id @GeneratedValue Long id; 17 | private Long fromUserId; 18 | private Long toUserId; 19 | private String currency; 20 | private BigDecimal amount; 21 | private Date transferTime; 22 | 23 | private Transfer() { 24 | } 25 | 26 | public Transfer(Long fromUserId, Long toUserId, String currency, BigDecimal amount) { 27 | this.fromUserId = fromUserId; 28 | this.toUserId = toUserId; 29 | this.currency = currency; 30 | this.amount = amount; 31 | this.transferTime = new Date(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/repository/TransactionRepository.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 9 | 10 | import com.demo.bankapp.model.Transaction; 11 | 12 | @RepositoryRestResource(exported = false) 13 | public interface TransactionRepository extends JpaRepository { 14 | 15 | @Query(value = "SELECT COUNT(*) FROM Transaction WHERE userId = :userId and transactionTime >= DATEADD(day, -1, GETDATE())") 16 | int getOperationCountFromLast24Hours(@Param("userId") Long userId); 17 | 18 | List findAllByUserId(Long userId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/model/Transaction.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | 10 | import lombok.Data; 11 | 12 | @Data 13 | @Entity 14 | public class Transaction { 15 | 16 | private @Id @GeneratedValue Long id; 17 | private Long userId; 18 | private boolean isBought; 19 | private String currency; 20 | private BigDecimal amount; 21 | private Date transactionTime; 22 | 23 | private Transaction() { 24 | } 25 | 26 | public Transaction(Long userId, boolean isBought, String currency, BigDecimal amount) { 27 | this.userId = userId; 28 | this.isBought = isBought; 29 | this.currency = currency; 30 | this.amount = amount; 31 | this.transactionTime = new Date(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/config/TestUtils.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.config; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.test.web.servlet.RequestBuilder; 5 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 6 | 7 | public class TestUtils { 8 | 9 | public static RequestBuilder getPostRequestBuilder(String url, String requestAsJson) { 10 | return MockMvcRequestBuilders 11 | .post(url) 12 | .accept(MediaType.APPLICATION_JSON) 13 | .content(requestAsJson) 14 | .contentType(MediaType.APPLICATION_JSON) 15 | .characterEncoding("utf-8"); 16 | } 17 | 18 | public static RequestBuilder getGetRequestBuilder(String url) { 19 | return MockMvcRequestBuilders 20 | .get(url) 21 | .accept(MediaType.APPLICATION_JSON) 22 | .contentType(MediaType.APPLICATION_JSON) 23 | .characterEncoding("utf-8"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/concretions/TransferService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.concretions; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import com.demo.bankapp.model.Transfer; 9 | import com.demo.bankapp.repository.TransferRepository; 10 | import com.demo.bankapp.service.abstractions.ITransferService; 11 | 12 | @Service 13 | public class TransferService implements ITransferService { 14 | 15 | private TransferRepository repository; 16 | 17 | @Autowired 18 | public TransferService(TransferRepository repository) { 19 | this.repository = repository; 20 | } 21 | 22 | @Override 23 | public Transfer createNewTransfer(Transfer transfer) { 24 | return repository.save(transfer); 25 | } 26 | 27 | @Override 28 | public List findAllTransfersFrom24Hours(Long userId) { 29 | return repository.findAllTransfersFrom24Hours(userId); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/Constants.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration; 2 | 3 | public final class Constants { 4 | 5 | private Constants() {} 6 | 7 | public static final String MAIN_CURRENCY = "TRY"; 8 | 9 | public static final String MESSAGE_INVALIDUSERNAME = "Invalid username."; 10 | public static final String MESSAGE_INVALIDPASSWORD = "Invalid password."; 11 | public static final String MESSAGE_INVALIDCURRENCY = "Invalid currency."; 12 | public static final String MESSAGE_INVALIDAMOUNT = "Invalid amount."; 13 | public static final String MESSAGE_INVALIDTCNO = "Invalid TC No."; 14 | public static final String MESSAGE_EXCEEDEDMAXVALUE = "Exceeded Maximum Value Per Transaction."; 15 | public static final String MESSAGE_EXCEEDEDMAXVALUEFORDAY = "Exceeded Maximum Transaction Value For the Day."; 16 | public static final String MESSAGE_SAMEUSERTRANSACTION = "You can't send money to yourself."; 17 | public static final String MESSAGE_SAMEUSERNAMEEXIST = "User with the same name exists."; 18 | public static final String MESSAGE_SAMETCNOEXIST = "User with the same tc no exists."; 19 | public static final String MESSAGE_EXCHANGESWITHMAINCURRENCY = "You can't make exchange transactions with " + MAIN_CURRENCY; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/concretions/TransactionService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.concretions; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import com.demo.bankapp.model.Transaction; 10 | import com.demo.bankapp.repository.TransactionRepository; 11 | import com.demo.bankapp.service.abstractions.ITransactionService; 12 | 13 | @Service 14 | public class TransactionService implements ITransactionService { 15 | 16 | private TransactionRepository repository; 17 | 18 | @Autowired 19 | public TransactionService(TransactionRepository repository) { 20 | this.repository = repository; 21 | } 22 | 23 | @Override 24 | public Transaction createNewTransaction(Long userId, boolean isBuying, String currency, BigDecimal amount) { 25 | Transaction transaction = new Transaction(userId, isBuying, currency, amount); 26 | return repository.save(transaction); 27 | } 28 | 29 | @Override 30 | public int getOperationCountFromLast24Hours(Long userId) { 31 | return repository.getOperationCountFromLast24Hours(userId); 32 | } 33 | 34 | @Override 35 | public List findAllByUserId(Long userId) { 36 | return repository.findAllByUserId(userId); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/DatabaseMocker.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.Ordered; 7 | import org.springframework.core.annotation.Order; 8 | 9 | import com.demo.bankapp.controller.UserController; 10 | import com.demo.bankapp.repository.UserRepository; 11 | import com.demo.bankapp.request.CreateUserRequest; 12 | 13 | @Configuration 14 | class DatabaseMocker { 15 | 16 | @Bean 17 | @Order(Ordered.HIGHEST_PRECEDENCE) 18 | CommandLineRunner initDatabase(UserRepository repository, UserController userController) { 19 | return args -> { 20 | CreateUserRequest cnuRequest = new CreateUserRequest(); 21 | cnuRequest.setUsername("Mert"); 22 | cnuRequest.setPassword("mert123"); 23 | cnuRequest.setTcno("21412322112"); 24 | 25 | CreateUserRequest cnuRequest2 = new CreateUserRequest(); 26 | cnuRequest2.setUsername("Mert2"); 27 | cnuRequest2.setPassword("mert1234"); 28 | cnuRequest2.setTcno("23141232212"); 29 | 30 | CreateUserRequest cnuRequest3 = new CreateUserRequest(); 31 | cnuRequest3.setUsername("Mert3"); 32 | cnuRequest3.setPassword("mert12345"); 33 | cnuRequest3.setTcno("23141232213"); 34 | 35 | userController.createUser(cnuRequest); 36 | userController.createUser(cnuRequest2); 37 | userController.createUser(cnuRequest3); 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/controller/WealthController.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import com.demo.bankapp.configuration.Constants; 11 | import com.demo.bankapp.exception.BadRequestException; 12 | import com.demo.bankapp.model.User; 13 | import com.demo.bankapp.model.Wealth; 14 | import com.demo.bankapp.request.RetrieveWealthRequest; 15 | import com.demo.bankapp.response.RetrieveWealthResponse; 16 | import com.demo.bankapp.service.abstractions.IUserService; 17 | import com.demo.bankapp.service.abstractions.IWealthService; 18 | 19 | @RestController 20 | @RequestMapping(value = "/wealth", produces = { MediaType.APPLICATION_JSON_VALUE }) 21 | public class WealthController { 22 | 23 | private IWealthService wealthService; 24 | private IUserService userService; 25 | 26 | @Autowired 27 | public WealthController(IWealthService wealthService, IUserService userService) { 28 | this.wealthService = wealthService; 29 | this.userService = userService; 30 | } 31 | 32 | @PostMapping("/retrieve") 33 | public RetrieveWealthResponse retrieveWealth(@RequestBody RetrieveWealthRequest request) { 34 | 35 | if (request.getUsername() == null || request.getUsername().equals("")) { 36 | throw new BadRequestException(Constants.MESSAGE_INVALIDUSERNAME); 37 | } 38 | 39 | User user = userService.findByUserName(request.getUsername()); 40 | Wealth wealth = wealthService.findWealth(user.getId()); 41 | 42 | RetrieveWealthResponse response = new RetrieveWealthResponse(); 43 | response.setWealth(wealth); 44 | return response; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/repository/WealthRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.HashMap; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 12 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; 13 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 14 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 15 | import org.springframework.test.context.ActiveProfiles; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import com.demo.bankapp.model.Wealth; 19 | 20 | @ActiveProfiles("test") 21 | @RunWith(SpringRunner.class) 22 | @DataJpaTest 23 | @AutoConfigureTestDatabase(replace = Replace.NONE) 24 | public class WealthRepositoryTest { 25 | 26 | @Autowired 27 | private TestEntityManager entityManager; 28 | 29 | @Autowired 30 | private WealthRepository repository; 31 | 32 | @Test 33 | public void test() { 34 | int initialCountOfWealth = repository.findAll().size(); 35 | 36 | Wealth firstWealth = new Wealth(null, new HashMap()); 37 | entityManager.persist(firstWealth); 38 | entityManager.flush(); 39 | 40 | assertThat(repository.findAll().size()).isEqualTo(initialCountOfWealth + 1); 41 | 42 | Wealth wealthToSave = new Wealth(null, new HashMap()); 43 | Wealth savedWealth = repository.save(wealthToSave); 44 | 45 | assertThat(repository.findAll().size()).isEqualTo(initialCountOfWealth + 2); 46 | assertThat(savedWealth.getUserId()).isNotNull(); 47 | 48 | assertThat(repository.findById(savedWealth.getUserId())).isNotNull(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/repository/TransferRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 12 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; 13 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 14 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 15 | import org.springframework.test.context.ActiveProfiles; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import com.demo.bankapp.model.Transfer; 19 | 20 | @ActiveProfiles("test") 21 | @RunWith(SpringRunner.class) 22 | @DataJpaTest 23 | @AutoConfigureTestDatabase(replace = Replace.NONE) 24 | public class TransferRepositoryTest { 25 | 26 | @Autowired 27 | private TestEntityManager entityManager; 28 | 29 | @Autowired 30 | private TransferRepository repository; 31 | 32 | @Test 33 | public void test() { 34 | Transfer firstWealth = new Transfer(255526L, 521125L, "USD", BigDecimal.valueOf(251266)); 35 | entityManager.persist(firstWealth); 36 | entityManager.flush(); 37 | 38 | assertThat(repository.findAll().size()).isEqualTo(1); 39 | 40 | // save 41 | Transfer transferToSave = new Transfer(2556L, 5125L, "USD", BigDecimal.valueOf(2516)); 42 | Transfer savedTransfer = repository.save(transferToSave); 43 | 44 | assertThat(repository.findAll().size()).isEqualTo(2); 45 | assertThat(savedTransfer.getFromUserId()).isEqualTo(transferToSave.getFromUserId()); 46 | assertThat(savedTransfer.getToUserId()).isEqualTo(transferToSave.getToUserId()); 47 | assertThat(savedTransfer.getCurrency()).isEqualTo(transferToSave.getCurrency()); 48 | assertThat(savedTransfer.getAmount()).isEqualTo(transferToSave.getAmount()); 49 | 50 | // findAllTransfersFrom24Hours 51 | List transferList = repository.findAllTransfersFrom24Hours(savedTransfer.getFromUserId()); 52 | assertThat(transferList.size()).isEqualTo(1); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/service/TransferServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.mockito.Mockito; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 15 | 16 | import com.demo.bankapp.model.Transfer; 17 | import com.demo.bankapp.repository.TransferRepository; 18 | import com.demo.bankapp.service.concretions.TransferService; 19 | 20 | @RunWith(SpringJUnit4ClassRunner.class) 21 | public class TransferServiceTest { 22 | 23 | @MockBean 24 | private TransferRepository repository; 25 | 26 | private TransferService service; 27 | 28 | @Before 29 | public void setUp() { 30 | service = new TransferService(repository); 31 | } 32 | 33 | @Test 34 | public void createNewTransaction() { 35 | Transfer mockedTransfer = new Transfer(5816L, 2181L, "AUD", BigDecimal.valueOf(25125)); 36 | Mockito.when(repository.save(Mockito.any())).thenReturn(mockedTransfer); 37 | 38 | Transfer createdTransfer = service.createNewTransfer(mockedTransfer); 39 | 40 | assertThat(createdTransfer.getAmount()).isEqualTo(mockedTransfer.getAmount()); 41 | assertThat(createdTransfer.getCurrency()).isEqualTo(mockedTransfer.getCurrency()); 42 | assertThat(createdTransfer.getFromUserId()).isEqualTo(mockedTransfer.getFromUserId()); 43 | assertThat(createdTransfer.getToUserId()).isEqualTo(mockedTransfer.getToUserId()); 44 | } 45 | 46 | @Test 47 | public void findAllTransfersFrom24Hours() { 48 | List mockedTransferList = new ArrayList<>(); 49 | Transfer mockedTransfer = new Transfer(5816L, 2181L, "AUD", BigDecimal.valueOf(25125)); 50 | mockedTransferList.add(mockedTransfer); 51 | 52 | Mockito.when(repository.findAllTransfersFrom24Hours(mockedTransfer.getFromUserId())).thenReturn(mockedTransferList); 53 | List foundTransferList = service.findAllTransfersFrom24Hours(mockedTransfer.getFromUserId()); 54 | 55 | assertThat(foundTransferList).isEqualTo(mockedTransferList); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/security/JWTAuthorizationFilter.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration.security; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; 9 | 10 | import javax.servlet.FilterChain; 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | import static com.demo.bankapp.configuration.security.SecurityConstants.HEADER_STRING; 16 | import static com.demo.bankapp.configuration.security.SecurityConstants.SECRET; 17 | import static com.demo.bankapp.configuration.security.SecurityConstants.TOKEN_PREFIX; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | 22 | public class JWTAuthorizationFilter extends BasicAuthenticationFilter { 23 | 24 | public JWTAuthorizationFilter(AuthenticationManager authManager) { 25 | super(authManager); 26 | } 27 | 28 | @Override 29 | protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { 30 | String header = req.getHeader(HEADER_STRING); 31 | 32 | if (header == null || !header.startsWith(TOKEN_PREFIX)) { 33 | chain.doFilter(req, res); 34 | return; 35 | } 36 | 37 | UsernamePasswordAuthenticationToken authentication = getAuthentication(req); 38 | 39 | SecurityContextHolder.getContext().setAuthentication(authentication); 40 | chain.doFilter(req, res); 41 | } 42 | 43 | private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { 44 | String token = request.getHeader(HEADER_STRING); 45 | if (token != null) { 46 | // parse the token. 47 | String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes())).build().verify(token.replace(TOKEN_PREFIX, "")).getSubject(); 48 | 49 | if (user != null) { 50 | return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); 51 | } 52 | return null; 53 | } 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/repository/TransactionRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 12 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; 13 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 14 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 15 | import org.springframework.test.context.ActiveProfiles; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import com.demo.bankapp.model.Transaction; 19 | 20 | @ActiveProfiles("test") 21 | @RunWith(SpringRunner.class) 22 | @DataJpaTest 23 | @AutoConfigureTestDatabase(replace = Replace.NONE) 24 | public class TransactionRepositoryTest { 25 | 26 | @Autowired 27 | private TestEntityManager entityManager; 28 | 29 | @Autowired 30 | private TransactionRepository repository; 31 | 32 | @Test 33 | public void test() { 34 | Transaction firstTransaction = new Transaction(2515L, true, "EUR", BigDecimal.TEN); 35 | entityManager.persist(firstTransaction); 36 | entityManager.flush(); 37 | 38 | assertThat(repository.findAll().size()).isEqualTo(1); 39 | 40 | // save 41 | Transaction transactionToSave = new Transaction(162L, false, "USD", BigDecimal.valueOf(5126)); 42 | Transaction savedTransfer = repository.save(transactionToSave); 43 | 44 | assertThat(repository.findAll().size()).isEqualTo(2); 45 | assertThat(savedTransfer.getAmount()).isEqualTo(transactionToSave.getAmount()); 46 | assertThat(savedTransfer.getCurrency()).isEqualTo(transactionToSave.getCurrency()); 47 | assertThat(savedTransfer.getUserId()).isEqualTo(transactionToSave.getUserId()); 48 | assertThat(savedTransfer.getTransactionTime()).isEqualTo(transactionToSave.getTransactionTime()); 49 | 50 | // getOperationCountFromLast24Hours 51 | int operationCount = repository.getOperationCountFromLast24Hours(transactionToSave.getUserId()); 52 | assertThat(operationCount).isEqualTo(1); 53 | 54 | // findAllByUserId 55 | List byUserId = repository.findAllByUserId(transactionToSave.getUserId()); 56 | assertThat(byUserId.size()).isEqualTo(1); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/security/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration.security; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.config.http.SessionCreationPolicy; 10 | import org.springframework.security.core.userdetails.UserDetailsService; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.web.cors.CorsConfiguration; 13 | import org.springframework.web.cors.CorsConfigurationSource; 14 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 15 | 16 | @EnableWebSecurity 17 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 18 | 19 | private UserDetailsService userService; 20 | private PasswordEncoder passwordEncoder; 21 | 22 | @Autowired 23 | public SecurityConfig(UserDetailsService userService, PasswordEncoder passwordEncoder) { 24 | this.userService = userService; 25 | this.passwordEncoder = passwordEncoder; 26 | } 27 | 28 | @Override 29 | protected void configure(HttpSecurity http) throws Exception { 30 | http.cors().and().csrf().disable().authorizeRequests() 31 | .antMatchers("/user/create").permitAll() 32 | .anyRequest().authenticated() 33 | .and() 34 | .addFilter(new JWTAuthenticationFilter(authenticationManager())) 35 | .addFilter(new JWTAuthorizationFilter(authenticationManager())) 36 | // this disables session creation on Spring Security 37 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 38 | } 39 | 40 | @Override 41 | public void configure(AuthenticationManagerBuilder auth) throws Exception { 42 | auth.userDetailsService(userService).passwordEncoder(passwordEncoder); 43 | } 44 | 45 | @Bean 46 | CorsConfigurationSource corsConfigurationSource() { 47 | final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 48 | source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); 49 | return source; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/concretions/UserService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.concretions; 2 | 3 | import static java.util.Collections.emptyList; 4 | 5 | import java.util.List; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.stereotype.Service; 13 | 14 | import com.demo.bankapp.exception.UserNotFoundException; 15 | import com.demo.bankapp.model.User; 16 | import com.demo.bankapp.repository.UserRepository; 17 | import com.demo.bankapp.service.abstractions.IUserService; 18 | 19 | @Service 20 | public class UserService implements IUserService, UserDetailsService { 21 | 22 | private UserRepository repository; 23 | private PasswordEncoder passwordEncoder; 24 | 25 | @Autowired 26 | public UserService(UserRepository repository, PasswordEncoder passwordEncoder) { 27 | this.repository = repository; 28 | this.passwordEncoder = passwordEncoder; 29 | } 30 | 31 | @Override 32 | public List findAll() { 33 | return repository.findAll(); 34 | } 35 | 36 | @Override 37 | public User createNewUser(User user) { 38 | user.setPassword(passwordEncoder.encode(user.getPassword())); 39 | return repository.save(user); 40 | } 41 | 42 | @Override 43 | public User findByUserName(String username) { 44 | User user = repository.findByUsername(username); 45 | 46 | if (user == null) 47 | throw new UserNotFoundException(username); 48 | else 49 | return user; 50 | } 51 | 52 | @Override 53 | public User findByTcno(String tcno) { 54 | User user = repository.findByTcno(tcno); 55 | 56 | if (user == null) 57 | throw new UserNotFoundException("with TC No: " + tcno); 58 | else 59 | return user; 60 | } 61 | 62 | @Override 63 | public UserDetails loadUserByUsername(String username) { 64 | User user = repository.findByUsername(username); 65 | 66 | if (user == null) { 67 | throw new UsernameNotFoundException(username); 68 | } 69 | 70 | return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), emptyList()); 71 | } 72 | 73 | @Override 74 | public boolean isUsernameExist(String username) { 75 | User user = repository.findByUsername(username); 76 | return user != null; 77 | } 78 | 79 | @Override 80 | public boolean isTcnoExist(String tcno) { 81 | User user = repository.findByTcno(tcno); 82 | return user != null; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/service/TransactionServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.mockito.Mockito; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 15 | 16 | import com.demo.bankapp.model.Transaction; 17 | import com.demo.bankapp.repository.TransactionRepository; 18 | import com.demo.bankapp.service.concretions.TransactionService; 19 | 20 | @RunWith(SpringJUnit4ClassRunner.class) 21 | public class TransactionServiceTest { 22 | 23 | @MockBean 24 | private TransactionRepository repository; 25 | 26 | private TransactionService service; 27 | 28 | @Before 29 | public void setUp() { 30 | service = new TransactionService(repository); 31 | } 32 | 33 | @Test 34 | public void createNewTransaction() { 35 | Transaction mockedTransaction = new Transaction(25681L, true, "TRY", BigDecimal.valueOf(61268)); 36 | Mockito.when(repository.save(Mockito.any())).thenReturn(mockedTransaction); 37 | 38 | Transaction newTransaction = service.createNewTransaction(mockedTransaction.getUserId(), mockedTransaction.isBought(), mockedTransaction.getCurrency(), mockedTransaction.getAmount()); 39 | 40 | assertThat(mockedTransaction.getAmount()).isEqualTo(newTransaction.getAmount()); 41 | assertThat(mockedTransaction.getCurrency()).isEqualTo(newTransaction.getCurrency()); 42 | assertThat(mockedTransaction.getUserId()).isEqualTo(newTransaction.getUserId()); 43 | assertThat(mockedTransaction.isBought()).isEqualTo(newTransaction.isBought()); 44 | } 45 | 46 | @Test 47 | public void getOperationCountFromLast24Hours() { 48 | Mockito.when(repository.getOperationCountFromLast24Hours(Mockito.any())).thenReturn(20); 49 | 50 | int operationCount = service.getOperationCountFromLast24Hours(12161L); 51 | assertThat(operationCount).isPositive(); 52 | } 53 | 54 | @Test 55 | public void findAllByUserId() { 56 | List transactionList = new ArrayList<>(); 57 | Transaction mockedTransaction = new Transaction(61682L, true, "EUR", BigDecimal.valueOf(12661)); 58 | transactionList.add(mockedTransaction); 59 | 60 | Mockito.when(repository.findAllByUserId(Mockito.any())).thenReturn(transactionList); 61 | 62 | List foundTransactionList = service.findAllByUserId(mockedTransaction.getUserId()); 63 | assertThat(foundTransactionList).isEqualTo(transactionList); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/repository/UserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.repository; 2 | 3 | import java.util.List; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 9 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; 10 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 11 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 12 | import org.springframework.test.context.ActiveProfiles; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | import com.demo.bankapp.model.User; 18 | 19 | @ActiveProfiles("test") 20 | @RunWith(SpringRunner.class) 21 | @DataJpaTest 22 | @AutoConfigureTestDatabase(replace = Replace.NONE) 23 | public class UserRepositoryTest { 24 | 25 | @Autowired 26 | private TestEntityManager entityManager; 27 | 28 | @Autowired 29 | private UserRepository repository; 30 | 31 | @Test 32 | public void test() { 33 | 34 | int initialCountOfUsers = repository.findAll().size(); 35 | 36 | // findAll 37 | User firstUser = new User("Mert15", "mert123", "12453561256"); 38 | entityManager.persist(firstUser); 39 | entityManager.flush(); 40 | 41 | User secondUser = new User("Mert12", "mert125", "12455561256"); 42 | entityManager.persist(secondUser); 43 | entityManager.flush(); 44 | 45 | List users = repository.findAll(); 46 | 47 | assertThat(users.size()).isEqualTo(initialCountOfUsers + 2); 48 | assertThat(users.get(initialCountOfUsers + 0)).isEqualTo(firstUser); 49 | assertThat(users.get(initialCountOfUsers + 1)).isEqualTo(secondUser); 50 | 51 | // save 52 | User userToSave = new User("Saved_Mert", "mert1235", "12453571256"); 53 | User savedUser = repository.save(userToSave); 54 | 55 | assertThat(repository.findAll().size()).isEqualTo(initialCountOfUsers + 3); 56 | assertThat(userToSave.getUsername()).isEqualTo(savedUser.getUsername()); 57 | assertThat(userToSave.getTcno()).isEqualTo(savedUser.getTcno()); 58 | 59 | // findByUsername 60 | User foundByUsername = repository.findByUsername(firstUser.getUsername()); 61 | assertThat(firstUser.getUsername()).isEqualTo(foundByUsername.getUsername()); 62 | assertThat(firstUser.getPassword()).isEqualTo(foundByUsername.getPassword()); 63 | assertThat(firstUser.getTcno()).isEqualTo(foundByUsername.getTcno()); 64 | 65 | // findByTcno 66 | User foundByTcno = repository.findByTcno(firstUser.getTcno()); 67 | assertThat(firstUser.getUsername()).isEqualTo(foundByTcno.getUsername()); 68 | assertThat(firstUser.getPassword()).isEqualTo(foundByTcno.getPassword()); 69 | assertThat(firstUser.getTcno()).isEqualTo(foundByTcno.getTcno()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/configuration/security/JWTAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.configuration.security; 2 | 3 | import static com.auth0.jwt.algorithms.Algorithm.HMAC512; 4 | import static com.demo.bankapp.configuration.security.SecurityConstants.EXPIRATION_TIME; 5 | import static com.demo.bankapp.configuration.security.SecurityConstants.SECRET; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Date; 10 | 11 | import javax.servlet.FilterChain; 12 | import javax.servlet.ServletException; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | 16 | import org.springframework.security.authentication.AuthenticationManager; 17 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 18 | import org.springframework.security.core.Authentication; 19 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 20 | 21 | import com.auth0.jwt.JWT; 22 | import com.demo.bankapp.model.User; 23 | import com.fasterxml.jackson.databind.ObjectMapper; 24 | 25 | public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { 26 | 27 | private AuthenticationManager authenticationManager; 28 | 29 | public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { 30 | this.authenticationManager = authenticationManager; 31 | } 32 | 33 | @Override 34 | public Authentication attemptAuthentication(HttpServletRequest req, 35 | HttpServletResponse res) { 36 | 37 | try { 38 | User creds = new ObjectMapper().readValue(req.getInputStream(), User.class); 39 | 40 | return authenticationManager.authenticate( 41 | new UsernamePasswordAuthenticationToken( 42 | creds.getUsername(), 43 | creds.getPassword(), 44 | new ArrayList<>()) 45 | ); 46 | } catch (IOException e) { 47 | throw new RuntimeException(e); 48 | } 49 | } 50 | 51 | @Override 52 | protected void successfulAuthentication(HttpServletRequest req, 53 | HttpServletResponse res, 54 | FilterChain chain, 55 | Authentication auth) throws IOException, ServletException { 56 | 57 | String token = JWT.create() 58 | .withSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername()) 59 | .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) 60 | .sign(HMAC512(SECRET.getBytes())); 61 | // res.addHeader(HEADER_STRING, TOKEN_PREFIX + token); 62 | 63 | res.setStatus(HttpServletResponse.SC_OK); 64 | res.getWriter().write(token); 65 | res.getWriter().flush(); 66 | res.getWriter().close(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import java.util.List; 4 | import java.util.regex.Pattern; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import com.demo.bankapp.configuration.Constants; 15 | import com.demo.bankapp.exception.BadCredentialsException; 16 | import com.demo.bankapp.exception.BadRequestException; 17 | import com.demo.bankapp.model.User; 18 | import com.demo.bankapp.request.CreateUserRequest; 19 | import com.demo.bankapp.response.CreateUserResponse; 20 | import com.demo.bankapp.response.FindAllUsersResponse; 21 | import com.demo.bankapp.service.abstractions.IUserService; 22 | import com.demo.bankapp.service.abstractions.IWealthService; 23 | 24 | @RestController 25 | @RequestMapping(value = "/user", produces = { MediaType.APPLICATION_JSON_VALUE }) 26 | public class UserController { 27 | 28 | private IUserService userService; 29 | private IWealthService wealthService; 30 | 31 | @Autowired 32 | public UserController(IUserService userService, IWealthService wealthService) { 33 | this.userService = userService; 34 | this.wealthService = wealthService; 35 | } 36 | 37 | @GetMapping("/find/all") 38 | public FindAllUsersResponse findAll() { 39 | List userList = userService.findAll(); 40 | 41 | FindAllUsersResponse response = new FindAllUsersResponse(); 42 | response.setUserList(userList); 43 | return response; 44 | } 45 | 46 | @PostMapping("/create") 47 | public CreateUserResponse createUser(@RequestBody CreateUserRequest request) { 48 | 49 | if (request.getUsername() == null || request.getUsername().equals("")) { 50 | throw new BadRequestException(Constants.MESSAGE_INVALIDUSERNAME); 51 | } 52 | 53 | if (request.getPassword() == null || request.getPassword().equals("")) { 54 | throw new BadRequestException(Constants.MESSAGE_INVALIDPASSWORD); 55 | } 56 | 57 | if (request.getTcno() == null || request.getTcno().length() != 11 || !Pattern.matches("[0-9]+", request.getTcno())) { 58 | throw new BadRequestException(Constants.MESSAGE_INVALIDTCNO); 59 | } 60 | 61 | boolean isUsernameExist = userService.isUsernameExist(request.getUsername()); 62 | if (isUsernameExist) { 63 | throw new BadCredentialsException(Constants.MESSAGE_SAMEUSERNAMEEXIST); 64 | } 65 | 66 | boolean isTcnoExist = userService.isTcnoExist(request.getTcno()); 67 | if (isTcnoExist) { 68 | throw new BadCredentialsException(Constants.MESSAGE_SAMETCNOEXIST); 69 | } 70 | 71 | User user = userService.createNewUser(new User(request.getUsername(), request.getPassword(), request.getTcno())); 72 | wealthService.newWealthRecord(user.getId()); 73 | 74 | CreateUserResponse response = new CreateUserResponse(); 75 | response.setUsername(user.getUsername()); 76 | response.setTcno(user.getTcno()); 77 | return response; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.demo 13 | BankApplicationBackend 14 | 0.0.1-SNAPSHOT 15 | BankApplicationBackend 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-data-rest 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-hateoas 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-security 41 | 42 | 43 | org.springframework.security 44 | spring-security-jwt 45 | 1.0.7.RELEASE 46 | 47 | 48 | com.auth0 49 | java-jwt 50 | 3.4.0 51 | 52 | 53 | 54 | mysql 55 | mysql-connector-java 56 | 57 | 58 | 59 | com.h2database 60 | h2 61 | runtime 62 | 63 | 64 | org.projectlombok 65 | lombok 66 | true 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-test 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-maven-plugin 80 | 81 | 82 | org.jacoco 83 | jacoco-maven-plugin 84 | 0.7.6.201602180812 85 | 86 | 87 | prepare-agent 88 | 89 | prepare-agent 90 | 91 | 92 | 93 | 94 | 95 | org.eluder.coveralls 96 | coveralls-maven-plugin 97 | 4.3.0 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/service/WealthServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.Mockito; 12 | import org.springframework.boot.test.mock.mockito.MockBean; 13 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 14 | 15 | import com.demo.bankapp.exception.BadRequestException; 16 | import com.demo.bankapp.exception.InsufficientFundsException; 17 | import com.demo.bankapp.model.Wealth; 18 | import com.demo.bankapp.repository.WealthRepository; 19 | import com.demo.bankapp.service.concretions.WealthService; 20 | 21 | @RunWith(SpringJUnit4ClassRunner.class) 22 | public class WealthServiceTest { 23 | 24 | @MockBean 25 | private WealthRepository repository; 26 | 27 | private WealthService service; 28 | 29 | private Wealth mockedWealth; 30 | private Long mockedUserId; 31 | 32 | @Before 33 | public void setUp() { 34 | service = new WealthService(repository); 35 | 36 | Map mockedWealthMap = new HashMap<>(); 37 | mockedWealthMap.put("USD", BigDecimal.valueOf(2500)); 38 | mockedWealthMap.put("TRY", BigDecimal.valueOf(2000)); 39 | mockedWealthMap.put("EUR", BigDecimal.valueOf(3000)); 40 | mockedWealthMap.put("AUD", BigDecimal.ZERO); 41 | 42 | this.mockedUserId = 5125L; 43 | this.mockedWealth = new Wealth(mockedUserId, mockedWealthMap); 44 | 45 | Mockito.when(repository.findById(mockedUserId)).thenReturn(Optional.of(mockedWealth)); 46 | } 47 | 48 | @Test 49 | public void newWealthRecord() { 50 | service.newWealthRecord(25161L); 51 | } 52 | 53 | @Test 54 | public void makeWealthExchange() { 55 | service.makeWealthExchange(mockedUserId, "USD", BigDecimal.valueOf(150), true); 56 | service.makeWealthExchange(mockedUserId, "USD", BigDecimal.valueOf(250), false); 57 | } 58 | 59 | @Test(expected = InsufficientFundsException.class) 60 | public void makeWealthExchange_InsufficientFunds_Sell() { 61 | service.makeWealthExchange(mockedUserId, "USD", BigDecimal.valueOf(3000), false); 62 | } 63 | 64 | @Test(expected = InsufficientFundsException.class) 65 | public void makeWealthExchange_InsufficientFunds_Buy() { 66 | service.makeWealthExchange(mockedUserId, "USD", BigDecimal.valueOf(3000), true); 67 | } 68 | 69 | @Test(expected = BadRequestException.class) 70 | public void makeWealthExchange_InvalidCurrency() { 71 | service.makeWealthExchange(mockedUserId, "XSD", BigDecimal.valueOf(250), false); 72 | } 73 | 74 | @Test 75 | public void makeWealthTransaction() { 76 | service.makeWealthTransaction(mockedUserId, "EUR", BigDecimal.valueOf(2516), true); 77 | service.makeWealthTransaction(mockedUserId, "TRY", BigDecimal.valueOf(1000), false); 78 | } 79 | 80 | @Test(expected = InsufficientFundsException.class) 81 | public void makeWealthTransaction_InsufficientFunds() { 82 | service.makeWealthTransaction(mockedUserId, "TRY", BigDecimal.valueOf(5000), false); 83 | } 84 | 85 | @Test(expected = BadRequestException.class) 86 | public void makeWealthTransaction_InvalidCurrency() { 87 | service.makeWealthTransaction(mockedUserId, "DTD", BigDecimal.valueOf(250), false); 88 | } 89 | 90 | @Test 91 | public void findWealth() { 92 | service.findWealth(mockedUserId); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Spring Boot Application Example 4 | 5 | [![Build Status](https://travis-ci.org/mertakdut/Spring-Boot-Sample-Project.svg?branch=master)](https://travis-ci.org/mertakdut/Spring-Boot-Sample-Project) 6 | [![Coverage Status](https://coveralls.io/repos/github/mertakdut/Spring-Boot-Sample-Project/badge.svg?branch=master)](https://coveralls.io/github/mertakdut/Spring-Boot-Sample-Project?branch=master) 7 | 8 | This is a sample Java / Maven / Spring Boot application which provides RESTful services. It can be used as a starter project. Currently it is designed to work as [this project](https://github.com/mertakdut/React-Sample-Project)'s backend. 9 | 10 | ## Installation Instructions 11 | You can import the project as a maven application to your favorite IDE. I made my tests by using eclipse jee-2018-12. 12 | 13 | If lombok gets in your way, by referring [this answer](https://stackoverflow.com/a/22332248/4130569), you can install lombok by its jar file. 14 | 15 | ## To run the application 16 | Use one of the several ways of running a Spring Boot application. Below are just three options: 17 | 18 | 1. Build using maven goal (or by using maven wrapper): `mvn clean package` and execute the resulting artifact as follows `java -jar BankApplicationBackend-0.0.1-SNAPSHOT.jar` or 19 | 2. On Unix/Linux based systems: run `mvn clean package` then run the resulting jar as any other executable `./BankApplicationBackend-0.0.1-SNAPSHOT.jar` 20 | 3. Run as a [Docker](https://www.docker.com/) container. 21 | 1) Clone the repository. 22 | 2) cd to project root directory. 23 | 3) `docker build -t demo/bankapp .` 24 | * If you get a `./mvnw not found` error, just run `mvn -N io.takari:maven:wrapper -Dmaven=3.5.3` while in the root directory of the project. 25 | 4) `docker run --expose 8080 -p 8080:8080 demo/bankapp` 26 | 27 | ## To test the application 28 | 1. Create a user with /api/user/create url. 29 | 30 | `$ curl -X POST localhost:8080/api/user/create -d "{\"username\": \"yourUsername\", \"password\": \"yourPassword\", \"tcno\": \"12512561125\"}" -H "Content-Type:application/json"` 31 | You'll get a response as in below. 32 | 33 | `{"username":"yourUsername","tcno":"12512561125"}` 34 | 2. Generate an access token by /api/login url. 35 | 36 | `$ curl -H "Content-Type: application/json" -X POST -d "{\"username\": \"yourUsername\", \"password\": \"yourPassword\"}" http://localhost:8080/api/login` 37 | 38 | You'll be getting an access token similar to this. 39 | 40 | `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ5b3VyVXNlcm5hbWUiLCJleHAiOjE1NTI0NDMzNjZ9.0WSCg4vaP7BVeJz8tQnL3s-BYjBB6UWXlQKCZHm1_zqEVIiA8_71Ni7tbPDm2DbW-Qc_fPP9CQF1jKcRC9njFQ` 41 | 42 | 3. Use the token to access content available to all authenticated users, through the RESTful API. 43 | 44 | Http.Get request example: 45 | `curl -i -H "Accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ5b3VyVXNlcm5hbWUiLCJleHAiOjE1NTI0NDMzNjZ9.0WSCg4vaP7BVeJz8tQnL3s-BYjBB6UWXlQKCZHm1_zqEVIiA8_71Ni7tbPDm2DbW-Qc_fPP9CQF1jKcRC9njFQ" -X GET http://localhost:8080/api/user/find/all` 46 | 47 | Http.Post request example: 48 | `curl -H "Content-Type: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ5b3VyVXNlcm5hbWUiLCJleHAiOjE1NTI0NDMzNjZ9.0WSCg4vaP7BVeJz8tQnL3s-BYjBB6UWXlQKCZHm1_zqEVIiA8_71Ni7tbPDm2DbW-Qc_fPP9CQF1jKcRC9njFQ" -X POST -d "{\"username\": \"yourUsername\", \"buying\": \"true\", \"currency\": \"USD\", \"amount\": \"250\"}" http://localhost:8080/api/transaction/create` 49 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/controller/TransactionController.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import com.demo.bankapp.configuration.Constants; 13 | import com.demo.bankapp.exception.BadRequestException; 14 | import com.demo.bankapp.exception.DailyOperationLimitReachedException; 15 | import com.demo.bankapp.model.Transaction; 16 | import com.demo.bankapp.model.User; 17 | import com.demo.bankapp.request.CreateTransactionRequest; 18 | import com.demo.bankapp.request.FindAllTransactionsByUserRequest; 19 | import com.demo.bankapp.response.CreateTransactionResponse; 20 | import com.demo.bankapp.response.FindAllTransactionsByUserResponse; 21 | import com.demo.bankapp.service.abstractions.ITransactionService; 22 | import com.demo.bankapp.service.abstractions.IUserService; 23 | import com.demo.bankapp.service.abstractions.IWealthService; 24 | 25 | @RestController 26 | @RequestMapping(value="/transaction", produces = { MediaType.APPLICATION_JSON_VALUE }) 27 | public class TransactionController { 28 | 29 | private ITransactionService transactionService; 30 | private IUserService userService; 31 | private IWealthService wealthService; 32 | 33 | @Autowired 34 | public TransactionController(ITransactionService transactionService, IUserService userService, IWealthService wealthService) { 35 | this.transactionService = transactionService; 36 | this.userService = userService; 37 | this.wealthService = wealthService; 38 | } 39 | 40 | @PostMapping("/create") 41 | public CreateTransactionResponse createTransaction(@RequestBody CreateTransactionRequest request) { 42 | 43 | if (request.getUsername() == null || request.getUsername().equals("")) { 44 | throw new BadRequestException(Constants.MESSAGE_INVALIDUSERNAME); 45 | } else if (request.getCurrency() == null || request.getCurrency().equals("")) { 46 | throw new BadRequestException(Constants.MESSAGE_INVALIDCURRENCY); 47 | } else if (request.getAmount() == null || request.getAmount().signum() == 0 || request.getAmount().signum() == -1) { 48 | throw new BadRequestException(Constants.MESSAGE_INVALIDAMOUNT); 49 | } else if (request.getCurrency().equals(Constants.MAIN_CURRENCY)) { 50 | throw new BadRequestException(Constants.MESSAGE_EXCHANGESWITHMAINCURRENCY); 51 | } 52 | 53 | User user = userService.findByUserName(request.getUsername()); 54 | 55 | int last24HoursOperationCount = transactionService.getOperationCountFromLast24Hours(user.getId()); 56 | if (last24HoursOperationCount >= 10) { 57 | throw new DailyOperationLimitReachedException(); 58 | } 59 | 60 | wealthService.makeWealthExchange(user.getId(), request.getCurrency(), request.getAmount(), request.isBuying()); 61 | Transaction transaction = transactionService.createNewTransaction(user.getId(), request.isBuying(), request.getCurrency(), request.getAmount()); 62 | 63 | CreateTransactionResponse response = new CreateTransactionResponse(); 64 | response.setTransaction(transaction); 65 | return response; 66 | } 67 | 68 | @PostMapping("/find/all") 69 | public FindAllTransactionsByUserResponse findAll(@RequestBody FindAllTransactionsByUserRequest request) { 70 | 71 | if (request.getUsername() == null || request.getUsername().equals("")) { 72 | throw new BadRequestException(Constants.MESSAGE_INVALIDUSERNAME); 73 | } 74 | 75 | User user = userService.findByUserName(request.getUsername()); 76 | List transactionList = transactionService.findAllByUserId(user.getId()); 77 | 78 | FindAllTransactionsByUserResponse response = new FindAllTransactionsByUserResponse(); 79 | response.setTransactionList(transactionList); 80 | return response; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/controller/WealthControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import org.junit.ClassRule; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.Parameterized; 17 | import org.junit.runners.Parameterized.Parameters; 18 | import org.mockito.Mockito; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 21 | import org.springframework.boot.test.mock.mockito.MockBean; 22 | import org.springframework.test.context.junit4.rules.SpringClassRule; 23 | import org.springframework.test.context.junit4.rules.SpringMethodRule; 24 | import org.springframework.test.web.servlet.MockMvc; 25 | import org.springframework.test.web.servlet.RequestBuilder; 26 | import org.springframework.test.web.servlet.ResultActions; 27 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 28 | 29 | import com.demo.bankapp.config.TestUtils; 30 | import com.demo.bankapp.model.User; 31 | import com.demo.bankapp.model.Wealth; 32 | import com.demo.bankapp.request.RetrieveWealthRequest; 33 | import com.demo.bankapp.service.abstractions.IUserService; 34 | import com.demo.bankapp.service.abstractions.IWealthService; 35 | import com.fasterxml.jackson.databind.ObjectMapper; 36 | 37 | @RunWith(Parameterized.class) 38 | @WebMvcTest(value = WealthController.class, secure = false) 39 | public class WealthControllerTest { 40 | 41 | @ClassRule 42 | public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); 43 | 44 | @Rule 45 | public final SpringMethodRule springMethodRule = new SpringMethodRule(); 46 | 47 | @MockBean 48 | private IUserService userService; 49 | 50 | @MockBean 51 | private IWealthService wealthService; 52 | 53 | @Autowired 54 | private MockMvc mockMvc; 55 | 56 | private RetrieveWealthRequest request; 57 | 58 | public WealthControllerTest(RetrieveWealthRequest request) { 59 | this.request = request; 60 | } 61 | 62 | @Parameters 63 | public static List data() { 64 | RetrieveWealthRequest request1 = new RetrieveWealthRequest(); 65 | RetrieveWealthRequest request2 = new RetrieveWealthRequest(); 66 | request2.setUsername(""); 67 | RetrieveWealthRequest request3 = new RetrieveWealthRequest(); 68 | request3.setUsername("Mert"); 69 | 70 | List testCases = new ArrayList<>(); 71 | testCases.add(request1); 72 | testCases.add(request2); 73 | testCases.add(request3); 74 | 75 | return testCases; 76 | } 77 | 78 | @Test 79 | public void retrieveWealth() throws Exception { 80 | 81 | boolean shouldThrowBadRequest = request.getUsername() == null || request.getUsername().equals(""); 82 | 83 | Map mockedWealthMap = new HashMap<>(); 84 | mockedWealthMap.put("TRY", BigDecimal.valueOf(58212)); 85 | mockedWealthMap.put("EUR", BigDecimal.valueOf(5000)); 86 | mockedWealthMap.put("USD", BigDecimal.valueOf(1000)); 87 | 88 | Mockito.when(userService.findByUserName(Mockito.anyString())).thenReturn(new User(request.getUsername(), "mert123", "52812576921")); 89 | Mockito.when(wealthService.findWealth(Mockito.any())).thenReturn(new Wealth(5125L, mockedWealthMap)); 90 | 91 | String requestAsJson = new ObjectMapper().writeValueAsString(request); 92 | RequestBuilder requestBuilder = TestUtils.getPostRequestBuilder("/wealth/retrieve", requestAsJson); 93 | 94 | ResultActions resultActions = mockMvc.perform(requestBuilder); 95 | if(shouldThrowBadRequest) { 96 | resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); 97 | } else { 98 | resultActions.andExpect(MockMvcResultMatchers.status().isOk()) 99 | .andExpect(jsonPath("$.wealth.userId", equalTo(5125))) 100 | .andExpect(jsonPath("$.wealth.wealthMap.TRY", equalTo(58212))) 101 | .andExpect(jsonPath("$.wealth.wealthMap.EUR", equalTo(5000))) 102 | .andExpect(jsonPath("$.wealth.wealthMap.USD", equalTo(1000))); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.Mockito; 12 | import org.springframework.boot.test.mock.mockito.MockBean; 13 | import org.springframework.security.core.userdetails.UserDetails; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 16 | 17 | import com.demo.bankapp.model.User; 18 | import com.demo.bankapp.repository.UserRepository; 19 | import com.demo.bankapp.service.concretions.UserService; 20 | 21 | @RunWith(SpringJUnit4ClassRunner.class) 22 | public class UserServiceTest { 23 | 24 | @MockBean 25 | private UserRepository repository; 26 | 27 | @MockBean 28 | private PasswordEncoder passwordEncoder; 29 | 30 | private UserService service; 31 | 32 | @Before 33 | public void setUp() { 34 | service = new UserService(repository, passwordEncoder); 35 | } 36 | 37 | @Test 38 | public void findAll() { 39 | List mockedUserList = new ArrayList<>(); 40 | mockedUserList.add(new User("Mert", "mert123", "52125082721")); 41 | 42 | Mockito.when(repository.findAll()).thenReturn(mockedUserList); 43 | List foundUserList = service.findAll(); 44 | 45 | assertThat(foundUserList).isEqualTo(mockedUserList); 46 | } 47 | 48 | @Test 49 | public void createNewUser() { 50 | String mockedEncodedPassword = "EncodedPassword5128uawyr"; 51 | User mockedUser = new User("Mert", "mert123", "52125082721"); 52 | 53 | Mockito.when(passwordEncoder.encode(Mockito.anyString())).thenReturn(mockedEncodedPassword); 54 | Mockito.when(repository.save(Mockito.any())).thenReturn(mockedUser); 55 | 56 | User createdUser = service.createNewUser(mockedUser); 57 | 58 | assertThat(createdUser.getUsername()).isEqualTo(mockedUser.getUsername()); 59 | assertThat(createdUser.getPassword()).isEqualTo(mockedUser.getPassword()); 60 | assertThat(createdUser.getTcno()).isEqualTo(mockedUser.getTcno()); 61 | } 62 | 63 | @Test 64 | public void findByUserName() { 65 | User mockedUser = new User("Mert", "mert123", "52125082721"); 66 | Mockito.when(repository.findByUsername(Mockito.anyString())).thenReturn(mockedUser); 67 | 68 | User createdUser = service.findByUserName(mockedUser.getUsername()); 69 | 70 | assertThat(createdUser.getUsername()).isEqualTo(mockedUser.getUsername()); 71 | assertThat(createdUser.getPassword()).isEqualTo(mockedUser.getPassword()); 72 | assertThat(createdUser.getTcno()).isEqualTo(mockedUser.getTcno()); 73 | } 74 | 75 | @Test 76 | public void findByTcno() { 77 | User mockedUser = new User("Mert", "mert123", "52125082721"); 78 | Mockito.when(repository.findByTcno(Mockito.anyString())).thenReturn(mockedUser); 79 | 80 | User createdUser = service.findByTcno(mockedUser.getTcno()); 81 | 82 | assertThat(createdUser.getUsername()).isEqualTo(mockedUser.getUsername()); 83 | assertThat(createdUser.getPassword()).isEqualTo(mockedUser.getPassword()); 84 | assertThat(createdUser.getTcno()).isEqualTo(mockedUser.getTcno()); 85 | } 86 | 87 | @Test 88 | public void loadUserByUsername() { 89 | User mockedUser = new User("Mert", "mert123", "52125082721"); 90 | Mockito.when(repository.findByUsername(Mockito.anyString())).thenReturn(mockedUser); 91 | 92 | UserDetails loadedUser = service.loadUserByUsername(mockedUser.getUsername()); 93 | 94 | assertThat(loadedUser.getUsername()).isEqualTo(mockedUser.getUsername()); 95 | assertThat(loadedUser.getPassword()).isEqualTo(mockedUser.getPassword()); 96 | assertThat(loadedUser).isExactlyInstanceOf(org.springframework.security.core.userdetails.User.class); 97 | } 98 | 99 | @Test 100 | public void isUsernameExist() { 101 | User mockedUser = new User("Mert", "mert123", "52125082721"); 102 | Mockito.when(repository.findByUsername(mockedUser.getUsername())).thenReturn(mockedUser); 103 | 104 | boolean isUsernameExist = service.isUsernameExist(mockedUser.getUsername()); 105 | assertThat(isUsernameExist).isEqualTo(true); 106 | } 107 | 108 | @Test 109 | public void isTcNoExist() { 110 | User mockedUser = new User("Mert", "mert123", "52125082721"); 111 | Mockito.when(repository.findByTcno(mockedUser.getTcno())).thenReturn(mockedUser); 112 | 113 | boolean isTcnoExist = service.isTcnoExist(mockedUser.getTcno()); 114 | assertThat(isTcnoExist).isEqualTo(true); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/service/concretions/WealthService.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.service.concretions; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | import com.demo.bankapp.configuration.Constants; 13 | import com.demo.bankapp.exception.BadRequestException; 14 | import com.demo.bankapp.exception.InsufficientFundsException; 15 | import com.demo.bankapp.exception.UserNotFoundException; 16 | import com.demo.bankapp.model.Wealth; 17 | import com.demo.bankapp.repository.WealthRepository; 18 | import com.demo.bankapp.service.abstractions.IWealthService; 19 | 20 | @Service 21 | public class WealthService implements IWealthService { 22 | 23 | private WealthRepository repository; 24 | 25 | @Autowired 26 | public WealthService(WealthRepository repository) { 27 | this.repository = repository; 28 | } 29 | 30 | @Override 31 | public void newWealthRecord(Long userId) { 32 | 33 | Map wealthMap = new HashMap<>(); 34 | 35 | Map currencyMap = getCurrencyRates(); 36 | for (Map.Entry entry : currencyMap.entrySet()) { 37 | wealthMap.put(entry.getKey(), BigDecimal.ZERO); 38 | } 39 | 40 | addInitialBalance(wealthMap); 41 | 42 | Wealth userWealth = new Wealth(userId, wealthMap); 43 | repository.save(userWealth); 44 | } 45 | 46 | @Override 47 | public void makeWealthExchange(Long userId, String currency, BigDecimal amount, boolean isBuying) { 48 | 49 | Wealth userWealth = repository.findById(userId).orElseThrow(() -> new UserNotFoundException()); 50 | Map wealthMap = userWealth.getWealthMap(); 51 | 52 | if (!wealthMap.containsKey(currency)) { 53 | throw new BadRequestException("Invalid currency."); 54 | } 55 | 56 | BigDecimal rate = BigDecimal.valueOf(getCurrencyRates().get(currency)); 57 | BigDecimal tryEquivalent = amount.divide(rate, 9, RoundingMode.HALF_UP); 58 | 59 | if (isBuying) { 60 | if (tryEquivalent.compareTo(wealthMap.get(Constants.MAIN_CURRENCY)) == 1) { // Trying to buy more than he can. 61 | throw new InsufficientFundsException(); 62 | } 63 | } else { 64 | if (amount.compareTo(wealthMap.get(currency)) == 1) { // Trying to sell more than he has. 65 | throw new InsufficientFundsException(currency); 66 | } 67 | } 68 | 69 | if (isBuying) { 70 | wealthMap.put(Constants.MAIN_CURRENCY, wealthMap.get(Constants.MAIN_CURRENCY).subtract(tryEquivalent)); 71 | wealthMap.put(currency, wealthMap.get(currency).add(amount)); 72 | } else { 73 | wealthMap.put(currency, wealthMap.get(currency).subtract(amount)); 74 | wealthMap.put(Constants.MAIN_CURRENCY, wealthMap.get(Constants.MAIN_CURRENCY).add(tryEquivalent)); 75 | } 76 | 77 | userWealth.setWealthMap(wealthMap); 78 | repository.save(userWealth); 79 | } 80 | 81 | @Override 82 | public void makeWealthTransaction(Long userId, String currency, BigDecimal amount, boolean isIncrementing) { 83 | 84 | Wealth userWealth = repository.findById(userId).orElseThrow(() -> new UserNotFoundException()); 85 | Map wealthMap = userWealth.getWealthMap(); 86 | 87 | if (!wealthMap.containsKey(currency)) { 88 | throw new BadRequestException(Constants.MESSAGE_INVALIDCURRENCY); 89 | } 90 | 91 | if (!isIncrementing) { 92 | if (amount.compareTo(wealthMap.get(currency)) == 1) { 93 | throw new InsufficientFundsException(currency); 94 | } 95 | 96 | wealthMap.put(currency, wealthMap.get(currency).subtract(amount)); 97 | } else { 98 | wealthMap.put(currency, wealthMap.get(currency).add(amount)); 99 | } 100 | 101 | userWealth.setWealthMap(wealthMap); 102 | repository.save(userWealth); 103 | } 104 | 105 | @Override 106 | public Wealth findWealth(Long userId) { 107 | return repository.findById(userId).orElseThrow(() -> new UserNotFoundException()); 108 | } 109 | 110 | @Override 111 | public Map getCurrencyRates() { 112 | final String uri = "https://api.exchangeratesapi.io/latest?base=TRY"; 113 | 114 | RestTemplate restTemplate = new RestTemplate(); 115 | return ((Map>) restTemplate.getForObject(uri, Map.class)).get("rates"); 116 | } 117 | 118 | private void addInitialBalance(Map wealthMap) { 119 | String currency = Constants.MAIN_CURRENCY; 120 | 121 | BigDecimal currentAmount = wealthMap.get(currency); 122 | BigDecimal amountToAdd = new BigDecimal(130000); 123 | BigDecimal finalAmount = currentAmount.add(amountToAdd); 124 | 125 | wealthMap.put(currency, finalAmount); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/controller/TransferController.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import com.demo.bankapp.configuration.Constants; 16 | import com.demo.bankapp.exception.BadRequestException; 17 | import com.demo.bankapp.exception.TransactionLimitException; 18 | import com.demo.bankapp.model.Transfer; 19 | import com.demo.bankapp.model.User; 20 | import com.demo.bankapp.request.CreateTransferRequest; 21 | import com.demo.bankapp.response.CreateTransferResponse; 22 | import com.demo.bankapp.service.abstractions.ITransferService; 23 | import com.demo.bankapp.service.abstractions.IUserService; 24 | import com.demo.bankapp.service.abstractions.IWealthService; 25 | 26 | @RestController 27 | @RequestMapping(value = "/transfer", produces = { MediaType.APPLICATION_JSON_VALUE }) 28 | public class TransferController { 29 | 30 | private IUserService userService; 31 | private IWealthService wealthService; 32 | private ITransferService transferService; 33 | 34 | @Autowired 35 | public TransferController(IUserService userService, IWealthService wealthService, ITransferService transferService) { 36 | this.userService = userService; 37 | this.wealthService = wealthService; 38 | this.transferService = transferService; 39 | } 40 | 41 | @PostMapping("/create") 42 | public CreateTransferResponse createTransfer(@RequestBody CreateTransferRequest request) { 43 | 44 | if (request.getCurrency() == null || request.getCurrency().equals("")) { 45 | throw new BadRequestException(Constants.MESSAGE_INVALIDCURRENCY); 46 | } 47 | 48 | if (request.getSenderUsername() == null || request.getSenderUsername().equals("")) { 49 | throw new BadRequestException(Constants.MESSAGE_INVALIDUSERNAME); 50 | } 51 | 52 | if (request.getReceiverTcno() == null || request.getReceiverTcno().equals("") || request.getReceiverTcno().length() != 11) { 53 | throw new BadRequestException(); 54 | } 55 | 56 | if (request.getAmount() == null || request.getAmount().signum() == 0 || request.getAmount().signum() == -1) { 57 | throw new BadRequestException(Constants.MESSAGE_INVALIDAMOUNT); 58 | } 59 | 60 | Map currencyRates = wealthService.getCurrencyRates(); 61 | 62 | BigDecimal singleTransferLimit = new BigDecimal(20000); 63 | BigDecimal tryEquivalent = getTryEquivalent(currencyRates, request.getCurrency(), request.getAmount()); 64 | if (tryEquivalent.compareTo(singleTransferLimit) == 1) { 65 | throw new TransactionLimitException(Constants.MESSAGE_EXCEEDEDMAXVALUE); 66 | } 67 | 68 | User senderUser = userService.findByUserName(request.getSenderUsername()); 69 | 70 | List last24HourTransfers = transferService.findAllTransfersFrom24Hours(senderUser.getId()); 71 | checkDailyTransferLimitExceedition(currencyRates, last24HourTransfers, tryEquivalent); 72 | 73 | User receiverUser = userService.findByTcno(request.getReceiverTcno()); 74 | 75 | if (senderUser.equals(receiverUser)) { 76 | throw new BadRequestException(Constants.MESSAGE_SAMEUSERTRANSACTION); 77 | } 78 | 79 | wealthService.makeWealthTransaction(senderUser.getId(), request.getCurrency(), request.getAmount(), false); 80 | wealthService.makeWealthTransaction(receiverUser.getId(), request.getCurrency(), request.getAmount(), true); 81 | 82 | Transfer transfer = transferService.createNewTransfer(new Transfer(senderUser.getId(), receiverUser.getId(), request.getCurrency(), request.getAmount())); 83 | 84 | CreateTransferResponse response = new CreateTransferResponse(); 85 | response.setTransfer(transfer); 86 | return response; 87 | } 88 | 89 | private BigDecimal getTryEquivalent(Map currencyRates, String currency, BigDecimal amount) { 90 | BigDecimal transferCurrRate = BigDecimal.valueOf(currencyRates.get(currency)); 91 | return amount.divide(transferCurrRate, 9, RoundingMode.HALF_UP); 92 | } 93 | 94 | private void checkDailyTransferLimitExceedition(Map currencyRates, List last24HourTransfers, BigDecimal transferTryEquivalent) { 95 | BigDecimal dailyTransferLimit = new BigDecimal(100000); 96 | 97 | BigDecimal rate; 98 | BigDecimal tryEquivalent; 99 | for (Transfer transfer : last24HourTransfers) { 100 | rate = BigDecimal.valueOf(currencyRates.get(transfer.getCurrency())); 101 | tryEquivalent = transfer.getAmount().divide(rate, 9, RoundingMode.HALF_UP); 102 | 103 | transferTryEquivalent = transferTryEquivalent.add(tryEquivalent); 104 | if (transferTryEquivalent.compareTo(dailyTransferLimit) == 1) { 105 | throw new TransactionLimitException(Constants.MESSAGE_EXCEEDEDMAXVALUEFORDAY); 106 | } 107 | } 108 | 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.3"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/demo/bankapp/exception/configuration/RestExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.exception.configuration; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.validation.ConstraintViolation; 7 | import javax.validation.ConstraintViolationException; 8 | 9 | import org.springframework.beans.TypeMismatchException; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.HttpRequestMethodNotSupportedException; 14 | import org.springframework.web.bind.annotation.ControllerAdvice; 15 | import org.springframework.web.bind.annotation.ExceptionHandler; 16 | import org.springframework.web.context.request.WebRequest; 17 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; 18 | import org.springframework.web.servlet.NoHandlerFoundException; 19 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 20 | 21 | import com.demo.bankapp.exception.BadCredentialsException; 22 | import com.demo.bankapp.exception.BadRequestException; 23 | import com.demo.bankapp.exception.DailyOperationLimitReachedException; 24 | import com.demo.bankapp.exception.InsufficientFundsException; 25 | import com.demo.bankapp.exception.TransactionLimitException; 26 | import com.demo.bankapp.exception.UserNotFoundException; 27 | 28 | @ControllerAdvice 29 | public class RestExceptionHandler extends ResponseEntityExceptionHandler { 30 | 31 | @Override 32 | protected ResponseEntity handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, 33 | final WebRequest request) { 34 | final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL(); 35 | 36 | final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), error); 37 | return buildResponseEntity(ex, apiError); 38 | } 39 | 40 | @Override 41 | protected ResponseEntity handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { 42 | final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType(); 43 | 44 | final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); 45 | return buildResponseEntity(ex, apiError); 46 | } 47 | 48 | @Override 49 | protected ResponseEntity handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, 50 | final WebRequest request) { 51 | final StringBuilder builder = new StringBuilder(); 52 | builder.append(ex.getMethod()); 53 | builder.append(" method is not supported for this request. Supported methods are "); 54 | ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " ")); 55 | 56 | final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage(), builder.toString()); 57 | return buildResponseEntity(ex, apiError); 58 | } 59 | 60 | @ExceptionHandler({ MethodArgumentTypeMismatchException.class }) 61 | public ResponseEntity handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, final WebRequest request) { 62 | final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName(); 63 | final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); 64 | return buildResponseEntity(ex, apiError); 65 | } 66 | 67 | @ExceptionHandler({ ConstraintViolationException.class }) 68 | public ResponseEntity handleConstraintViolation(final ConstraintViolationException ex, final WebRequest request) { 69 | final List errors = new ArrayList<>(); 70 | for (final ConstraintViolation violation : ex.getConstraintViolations()) { 71 | errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage()); 72 | } 73 | 74 | final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors); 75 | return buildResponseEntity(ex, apiError); 76 | } 77 | 78 | // Custom Exception Handlers -> Add next ones after these methods. 79 | 80 | @ExceptionHandler({ BadCredentialsException.class }) 81 | public ResponseEntity handleBadCredentials(final BadCredentialsException ex, final WebRequest request) { 82 | final ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, ex.getLocalizedMessage(), "error occurred"); 83 | return buildResponseEntity(ex, apiError); 84 | } 85 | 86 | @ExceptionHandler({ BadRequestException.class }) 87 | public ResponseEntity handleBadRequest(final BadRequestException ex, final WebRequest request) { 88 | final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), "error occurred"); 89 | return buildResponseEntity(ex, apiError); 90 | } 91 | 92 | @ExceptionHandler({ UserNotFoundException.class }) 93 | public ResponseEntity handleUserNotFound(final UserNotFoundException ex, final WebRequest request) { 94 | final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), "error occurred"); 95 | return buildResponseEntity(ex, apiError); 96 | } 97 | 98 | @ExceptionHandler({ InsufficientFundsException.class }) 99 | public ResponseEntity handle(final InsufficientFundsException ex, final WebRequest request) { 100 | final ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, ex.getLocalizedMessage(), "error occurred"); 101 | return buildResponseEntity(ex, apiError); 102 | } 103 | 104 | @ExceptionHandler({ TransactionLimitException.class }) 105 | public ResponseEntity handle(final TransactionLimitException ex, final WebRequest request) { 106 | final ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, ex.getLocalizedMessage(), "error occurred"); 107 | return buildResponseEntity(ex, apiError); 108 | } 109 | 110 | @ExceptionHandler({ DailyOperationLimitReachedException.class }) 111 | public ResponseEntity handle(final DailyOperationLimitReachedException ex, final WebRequest request) { 112 | final ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, ex.getLocalizedMessage(), "error occurred"); 113 | return buildResponseEntity(ex, apiError); 114 | } 115 | 116 | private ResponseEntity buildResponseEntity(Exception ex, ApiError apiError) { 117 | logger.info(ex.getClass().getName()); 118 | logger.error("error", ex); 119 | 120 | return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | echo Found %WRAPPER_JAR% 133 | ) else ( 134 | if not "%MVNW_REPOURL%" == "" ( 135 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" 136 | ) 137 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 138 | echo Downloading from: %DOWNLOAD_URL% 139 | 140 | powershell -Command "&{"^ 141 | "$webclient = new-object System.Net.WebClient;"^ 142 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 143 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 144 | "}"^ 145 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 146 | "}" 147 | echo Finished downloading %WRAPPER_JAR% 148 | ) 149 | @REM End of extension 150 | 151 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 152 | if ERRORLEVEL 1 goto error 153 | goto end 154 | 155 | :error 156 | set ERROR_CODE=1 157 | 158 | :end 159 | @endlocal & set ERROR_CODE=%ERROR_CODE% 160 | 161 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 162 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 163 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 164 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 165 | :skipRcPost 166 | 167 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 168 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 169 | 170 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 171 | 172 | exit /B %ERROR_CODE% 173 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/controller/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.regex.Pattern; 9 | 10 | import org.junit.ClassRule; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.junit.runners.Parameterized; 15 | import org.junit.runners.Parameterized.Parameters; 16 | import org.mockito.Mockito; 17 | import static org.hamcrest.collection.IsCollectionWithSize.hasSize; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 20 | import org.springframework.boot.test.mock.mockito.MockBean; 21 | import org.springframework.test.context.junit4.rules.SpringClassRule; 22 | import org.springframework.test.context.junit4.rules.SpringMethodRule; 23 | import org.springframework.test.web.servlet.MockMvc; 24 | import org.springframework.test.web.servlet.RequestBuilder; 25 | import org.springframework.test.web.servlet.ResultActions; 26 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 27 | 28 | import com.demo.bankapp.config.TestUtils; 29 | import com.demo.bankapp.model.User; 30 | import com.demo.bankapp.request.CreateUserRequest; 31 | import com.demo.bankapp.service.abstractions.IUserService; 32 | import com.demo.bankapp.service.abstractions.IWealthService; 33 | import com.fasterxml.jackson.databind.ObjectMapper; 34 | 35 | @RunWith(Parameterized.class) 36 | @WebMvcTest(value = UserController.class, secure = false) 37 | public class UserControllerTest { 38 | 39 | @ClassRule 40 | public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); 41 | 42 | @Rule 43 | public final SpringMethodRule springMethodRule = new SpringMethodRule(); 44 | 45 | @MockBean 46 | private IUserService userService; 47 | 48 | @MockBean 49 | private IWealthService wealthService; 50 | 51 | @Autowired 52 | private MockMvc mockMvc; 53 | 54 | private CreateUserRequest request; 55 | 56 | public UserControllerTest(CreateUserRequest request) { 57 | this.request = request; 58 | } 59 | 60 | @Parameters 61 | public static List data() { 62 | CreateUserRequest request1 = new CreateUserRequest(); 63 | CreateUserRequest request2 = new CreateUserRequest(); 64 | request2.setUsername(""); 65 | CreateUserRequest request3 = new CreateUserRequest(); 66 | request3.setUsername("Mert"); 67 | CreateUserRequest request4 = new CreateUserRequest(); 68 | request4.setUsername("Mert"); 69 | request4.setPassword(""); 70 | CreateUserRequest request5 = new CreateUserRequest(); 71 | request5.setUsername("Mert"); 72 | request5.setPassword("mert123"); 73 | CreateUserRequest request6 = new CreateUserRequest(); 74 | request6.setUsername("Mert"); 75 | request6.setPassword("mert123"); 76 | request6.setTcno(""); 77 | CreateUserRequest request7 = new CreateUserRequest(); 78 | request7.setUsername("Mert"); 79 | request7.setPassword("mert123"); 80 | request7.setTcno("52126"); 81 | CreateUserRequest request8 = new CreateUserRequest(); 82 | request8.setUsername("Mert"); 83 | request8.setPassword("mert123"); 84 | request8.setTcno("sfhashg"); 85 | CreateUserRequest request9 = new CreateUserRequest(); 86 | request9.setUsername("Mert"); 87 | request9.setPassword("mert123"); 88 | request9.setTcno("sfhashg"); 89 | CreateUserRequest request10 = new CreateUserRequest(); 90 | request10.setUsername("exist"); 91 | request10.setPassword("mert123"); 92 | request10.setTcno("12512262539"); 93 | CreateUserRequest request11 = new CreateUserRequest(); 94 | request11.setUsername("Mert"); 95 | request11.setPassword("mert123"); 96 | request11.setTcno("55555555555"); 97 | CreateUserRequest request12 = new CreateUserRequest(); 98 | request12.setUsername("Mert"); 99 | request12.setPassword("mert123"); 100 | request12.setTcno("51258693850"); 101 | 102 | List testCases = new ArrayList<>(); 103 | testCases.add(request1); 104 | testCases.add(request2); 105 | testCases.add(request3); 106 | testCases.add(request4); 107 | testCases.add(request5); 108 | testCases.add(request6); 109 | testCases.add(request7); 110 | testCases.add(request8); 111 | testCases.add(request9); 112 | testCases.add(request10); 113 | testCases.add(request11); 114 | testCases.add(request12); 115 | 116 | return testCases; 117 | } 118 | 119 | @Test 120 | public void createUser() throws Exception { 121 | 122 | boolean shouldThrowBadRequest = request.getUsername() == null || request.getUsername().equals("") || request.getPassword() == null || request.getPassword().equals("") || 123 | request.getTcno() == null || request.getTcno().length() != 11 || !Pattern.matches("[0-9]+", request.getTcno()); 124 | boolean shouldThrowBadCredentials = false; 125 | 126 | if (request.getUsername() != null && request.getUsername().equals("exist")) { 127 | Mockito.when(userService.isUsernameExist(Mockito.anyString())).thenReturn(Boolean.TRUE); 128 | shouldThrowBadCredentials = true; 129 | } 130 | 131 | if (request.getTcno() != null && request.getTcno().equals("55555555555")) { 132 | Mockito.when(userService.isTcnoExist(Mockito.anyString())).thenReturn(Boolean.TRUE); 133 | shouldThrowBadCredentials = true; 134 | } 135 | 136 | User mockedUser = new User("Mert6", "12356", "52535128592"); 137 | Mockito.when(userService.createNewUser(Mockito.any())).thenReturn(mockedUser); 138 | 139 | String requestAsJson = new ObjectMapper().writeValueAsString(request); 140 | RequestBuilder requestBuilder = TestUtils.getPostRequestBuilder("/user/create", requestAsJson); 141 | 142 | ResultActions resultActions = mockMvc.perform(requestBuilder); 143 | if(shouldThrowBadRequest) { 144 | resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); 145 | } else if (shouldThrowBadCredentials) { 146 | resultActions.andExpect(MockMvcResultMatchers.status().isUnauthorized()); 147 | } else { 148 | resultActions.andExpect(MockMvcResultMatchers.status().isOk()) 149 | .andExpect(jsonPath("$.username", equalTo(mockedUser.getUsername()))) 150 | .andExpect(jsonPath("$.tcno", equalTo(mockedUser.getTcno()))); 151 | } 152 | } 153 | 154 | @Test 155 | public void findAll() throws Exception { 156 | 157 | List userList = new ArrayList<>(); 158 | User mockedUser = new User("Mert", "mert123", "51258692052"); 159 | userList.add(mockedUser); 160 | 161 | Mockito.when(userService.findAll()).thenReturn(userList); 162 | 163 | RequestBuilder requestBuilder = TestUtils.getGetRequestBuilder("/user/find/all"); 164 | mockMvc.perform(requestBuilder) 165 | .andExpect(MockMvcResultMatchers.status().isOk()) 166 | .andExpect(jsonPath("$.userList", hasSize(1))) 167 | .andExpect(jsonPath("$.userList.[0].username", equalTo(mockedUser.getUsername()))); 168 | 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/controller/TransactionControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.hamcrest.collection.IsCollectionWithSize.hasSize; 5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.List; 11 | 12 | import org.junit.ClassRule; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.Parameterized; 17 | import org.junit.runners.Parameterized.Parameters; 18 | import org.mockito.Mockito; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 21 | import org.springframework.boot.test.mock.mockito.MockBean; 22 | import org.springframework.test.context.junit4.rules.SpringClassRule; 23 | import org.springframework.test.context.junit4.rules.SpringMethodRule; 24 | import org.springframework.test.web.servlet.MockMvc; 25 | import org.springframework.test.web.servlet.RequestBuilder; 26 | import org.springframework.test.web.servlet.ResultActions; 27 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 28 | 29 | import com.demo.bankapp.config.TestUtils; 30 | import com.demo.bankapp.model.Transaction; 31 | import com.demo.bankapp.model.User; 32 | import com.demo.bankapp.request.CreateTransactionRequest; 33 | import com.demo.bankapp.request.FindAllTransactionsByUserRequest; 34 | import com.demo.bankapp.service.abstractions.ITransactionService; 35 | import com.demo.bankapp.service.abstractions.IUserService; 36 | import com.demo.bankapp.service.abstractions.IWealthService; 37 | import com.fasterxml.jackson.databind.ObjectMapper; 38 | 39 | @RunWith(Parameterized.class) 40 | @WebMvcTest(value = TransactionController.class, secure = false) 41 | public class TransactionControllerTest { 42 | 43 | @ClassRule 44 | public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); 45 | 46 | @Rule 47 | public final SpringMethodRule springMethodRule = new SpringMethodRule(); 48 | 49 | @MockBean 50 | private IUserService userService; 51 | 52 | @MockBean 53 | private ITransactionService transactionService; 54 | 55 | @MockBean 56 | private IWealthService wealthService; 57 | 58 | @Autowired 59 | private MockMvc mockMvc; 60 | 61 | private CreateTransactionRequest ctRequest; 62 | private FindAllTransactionsByUserRequest fatbuRequest; 63 | 64 | public TransactionControllerTest(CreateTransactionRequest ctRequest, FindAllTransactionsByUserRequest fatbuRequest) { 65 | this.ctRequest = ctRequest; 66 | this.fatbuRequest = fatbuRequest; 67 | } 68 | 69 | @Parameters 70 | public static Collection data() { 71 | 72 | Collection params = new ArrayList<>(); 73 | 74 | CreateTransactionRequest request2 = new CreateTransactionRequest(); 75 | CreateTransactionRequest request3 = new CreateTransactionRequest(); 76 | request3.setUsername("Mert"); 77 | CreateTransactionRequest request4 = new CreateTransactionRequest(); 78 | request4.setUsername("Mert"); 79 | request4.setCurrency("TRY"); 80 | CreateTransactionRequest request5 = new CreateTransactionRequest(); 81 | request5.setUsername("Mert"); 82 | request5.setCurrency("EUR"); 83 | request5.setAmount(BigDecimal.TEN); 84 | CreateTransactionRequest request6 = new CreateTransactionRequest(); 85 | request6.setUsername("Mert"); 86 | request6.setCurrency("USD"); 87 | request6.setAmount(BigDecimal.valueOf(1250)); 88 | CreateTransactionRequest request7 = new CreateTransactionRequest(); 89 | request7.setUsername("Mert"); 90 | request7.setCurrency("TRY"); 91 | request7.setAmount(BigDecimal.valueOf(1250)); 92 | CreateTransactionRequest request8 = new CreateTransactionRequest(); 93 | request8.setUsername("Mert"); 94 | request8.setCurrency("EUR"); 95 | request8.setAmount(BigDecimal.ZERO); 96 | CreateTransactionRequest request9 = new CreateTransactionRequest(); 97 | request9.setUsername("Mert"); 98 | request9.setCurrency("USD"); 99 | request9.setAmount(BigDecimal.valueOf(-1)); 100 | CreateTransactionRequest request10 = new CreateTransactionRequest(); 101 | request10.setUsername("Mert"); 102 | request10.setCurrency(""); 103 | request10.setAmount(BigDecimal.valueOf(-1)); 104 | CreateTransactionRequest request11 = new CreateTransactionRequest(); 105 | request11.setUsername(""); 106 | request11.setCurrency("XSD"); 107 | 108 | FindAllTransactionsByUserRequest fatbuRequest1 = new FindAllTransactionsByUserRequest(); 109 | FindAllTransactionsByUserRequest fatbuRequest2 = new FindAllTransactionsByUserRequest(); 110 | fatbuRequest2.setUsername(""); 111 | FindAllTransactionsByUserRequest fatbuRequest3 = new FindAllTransactionsByUserRequest(); 112 | fatbuRequest3.setUsername("Mert"); 113 | 114 | params.add(new Object[] {request2, fatbuRequest1}); 115 | params.add(new Object[] {request3, fatbuRequest2}); 116 | params.add(new Object[] {request4, fatbuRequest3}); 117 | params.add(new Object[] {request5, fatbuRequest3}); 118 | params.add(new Object[] {request6, fatbuRequest2}); 119 | params.add(new Object[] {request7, fatbuRequest1}); 120 | params.add(new Object[] {request8, fatbuRequest3}); 121 | params.add(new Object[] {request9, fatbuRequest3}); 122 | params.add(new Object[] {request10, fatbuRequest2}); 123 | params.add(new Object[] {request11, fatbuRequest1}); 124 | 125 | return params; 126 | } 127 | 128 | @Test 129 | public void createTransaction() throws Exception { 130 | boolean shouldThrowBadRequest = ctRequest == null || ctRequest.getUsername() == null || ctRequest.getUsername().equals("") || 131 | ctRequest.getCurrency() == null || ctRequest.getCurrency().equals("") || ctRequest.getCurrency().equals("TRY") || ctRequest.getAmount() == null || 132 | ctRequest.getAmount().signum() == 0 || ctRequest.getAmount().signum() == -1; 133 | 134 | boolean shouldThrowDailyOperationLimitExceeded = false; 135 | 136 | Transaction mockTransaction = new Transaction(250L, true, "USD", BigDecimal.TEN); 137 | 138 | int mockedOperationCount = ctRequest != null && ctRequest.getCurrency() != null && ctRequest.getCurrency().equals("EUR") ? 15 : 5; 139 | if(mockedOperationCount == 15) { 140 | shouldThrowDailyOperationLimitExceeded = true; 141 | } 142 | 143 | Mockito.when(userService.findByUserName(Mockito.anyString())).thenReturn(new User("Mert", "mert123", "22512567125")); 144 | Mockito.when(transactionService.getOperationCountFromLast24Hours(Mockito.any())).thenReturn(mockedOperationCount); 145 | Mockito.when(transactionService.createNewTransaction(Mockito.any(), Mockito.anyBoolean(), Mockito.anyString(), Mockito.any())).thenReturn(mockTransaction); 146 | 147 | String requestAsJson = new ObjectMapper().writeValueAsString(ctRequest); 148 | RequestBuilder requestBuilder = TestUtils.getPostRequestBuilder("/transaction/create", requestAsJson); 149 | 150 | ResultActions resultActions = mockMvc.perform(requestBuilder); 151 | if (shouldThrowBadRequest) { 152 | resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); 153 | } else if (shouldThrowDailyOperationLimitExceeded) { 154 | resultActions.andExpect(MockMvcResultMatchers.status().isUnauthorized()); 155 | } else { 156 | resultActions.andExpect(MockMvcResultMatchers.status().isOk()) 157 | .andExpect(jsonPath("$.transaction.amount", equalTo(mockTransaction.getAmount().intValue()))) 158 | .andExpect(jsonPath("$.transaction.currency", equalTo(mockTransaction.getCurrency()))); 159 | } 160 | 161 | } 162 | 163 | @Test 164 | public void findAllTransactions() throws Exception { 165 | List transactionList = new ArrayList<>(); 166 | Transaction mockTransaction = new Transaction(63L, false, "EUR", BigDecimal.TEN); 167 | transactionList.add(mockTransaction); 168 | 169 | Mockito.when(userService.findByUserName(Mockito.anyString())).thenReturn(new User("Mert", "mert123", "22512567125")); 170 | Mockito.when(transactionService.findAllByUserId(Mockito.any())).thenReturn(transactionList); 171 | 172 | boolean shouldThrowBadRequest = fatbuRequest.getUsername() == null || fatbuRequest.getUsername().equals(""); 173 | 174 | String requestAsJson = new ObjectMapper().writeValueAsString(fatbuRequest); 175 | RequestBuilder requestBuilder = TestUtils.getPostRequestBuilder("/transaction/find/all", requestAsJson); 176 | 177 | ResultActions resultActions = mockMvc.perform(requestBuilder); 178 | if (shouldThrowBadRequest) { 179 | resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); 180 | } else { 181 | resultActions.andExpect(MockMvcResultMatchers.status().isOk()) 182 | .andExpect(jsonPath("$.transactionList", hasSize(1))) 183 | .andExpect(jsonPath("$.transactionList[0].currency", equalTo(mockTransaction.getCurrency()))) 184 | .andExpect(jsonPath("$.transactionList[0].amount", equalTo(mockTransaction.getAmount().intValue()))); 185 | } 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/test/java/com/demo/bankapp/controller/TransferControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.demo.bankapp.controller; 2 | 3 | import static org.hamcrest.Matchers.equalTo; 4 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import org.junit.ClassRule; 13 | import org.junit.Rule; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | import org.junit.runners.Parameterized; 17 | import org.junit.runners.Parameterized.Parameters; 18 | import org.mockito.Mockito; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 21 | import org.springframework.boot.test.mock.mockito.MockBean; 22 | import org.springframework.test.context.junit4.rules.SpringClassRule; 23 | import org.springframework.test.context.junit4.rules.SpringMethodRule; 24 | import org.springframework.test.web.servlet.MockMvc; 25 | import org.springframework.test.web.servlet.RequestBuilder; 26 | import org.springframework.test.web.servlet.ResultActions; 27 | import org.springframework.test.web.servlet.result.MockMvcResultMatchers; 28 | 29 | import com.demo.bankapp.config.TestUtils; 30 | import com.demo.bankapp.model.Transfer; 31 | import com.demo.bankapp.model.User; 32 | import com.demo.bankapp.request.CreateTransferRequest; 33 | import com.demo.bankapp.service.abstractions.ITransactionService; 34 | import com.demo.bankapp.service.abstractions.IUserService; 35 | import com.demo.bankapp.service.abstractions.IWealthService; 36 | import com.demo.bankapp.service.concretions.TransferService; 37 | import com.fasterxml.jackson.databind.ObjectMapper; 38 | 39 | @RunWith(Parameterized.class) 40 | @WebMvcTest(value = TransferController.class, secure = false) 41 | public class TransferControllerTest { 42 | 43 | @ClassRule 44 | public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); 45 | 46 | @Rule 47 | public final SpringMethodRule springMethodRule = new SpringMethodRule(); 48 | 49 | @MockBean 50 | private TransferService transferService; 51 | 52 | @MockBean 53 | private IUserService userService; 54 | 55 | @MockBean 56 | private ITransactionService transactionService; 57 | 58 | @MockBean 59 | private IWealthService wealthService; 60 | 61 | @Autowired 62 | private MockMvc mockMvc; 63 | 64 | private CreateTransferRequest request; 65 | 66 | public TransferControllerTest(CreateTransferRequest request) { 67 | this.request = request; 68 | } 69 | 70 | @Parameters 71 | public static List data() { 72 | CreateTransferRequest request1 = new CreateTransferRequest(); 73 | CreateTransferRequest request2 = new CreateTransferRequest(); 74 | request2.setCurrency(""); 75 | CreateTransferRequest request3 = new CreateTransferRequest(); 76 | request3.setCurrency("USD"); 77 | CreateTransferRequest request4 = new CreateTransferRequest(); 78 | request4.setCurrency("USD"); 79 | request4.setSenderUsername(""); 80 | CreateTransferRequest request5 = new CreateTransferRequest(); 81 | request5.setCurrency("USD"); 82 | request5.setSenderUsername("Mert"); 83 | CreateTransferRequest request6 = new CreateTransferRequest(); 84 | request6.setCurrency("USD"); 85 | request6.setSenderUsername("Mert"); 86 | request6.setReceiverTcno(""); 87 | CreateTransferRequest request7 = new CreateTransferRequest(); 88 | request7.setCurrency("USD"); 89 | request7.setSenderUsername("Mert"); 90 | request7.setReceiverTcno("312561"); 91 | CreateTransferRequest request8 = new CreateTransferRequest(); 92 | request8.setCurrency("USD"); 93 | request8.setSenderUsername("Mert"); 94 | request8.setReceiverTcno("31256152521"); 95 | CreateTransferRequest request9 = new CreateTransferRequest(); 96 | request9.setCurrency("USD"); 97 | request9.setSenderUsername("Mert"); 98 | request9.setReceiverTcno("31256152521"); 99 | request9.setAmount(BigDecimal.ZERO); 100 | CreateTransferRequest request10 = new CreateTransferRequest(); 101 | request10.setCurrency("USD"); 102 | request10.setSenderUsername("Mert"); 103 | request10.setReceiverTcno("31256152521"); 104 | request10.setAmount(BigDecimal.valueOf(-250)); 105 | CreateTransferRequest request11 = new CreateTransferRequest(); 106 | request11.setCurrency("USD"); 107 | request11.setSenderUsername("Mert"); 108 | request11.setReceiverTcno("31256152521"); 109 | request11.setAmount(BigDecimal.valueOf(250)); 110 | CreateTransferRequest request12 = new CreateTransferRequest(); 111 | request12.setCurrency("USD"); 112 | request12.setSenderUsername("Mert"); 113 | request12.setReceiverTcno("31256152521"); 114 | request12.setAmount(BigDecimal.valueOf(20001)); 115 | CreateTransferRequest request13 = new CreateTransferRequest(); 116 | request13.setCurrency("EUR"); 117 | request13.setSenderUsername("Mert"); 118 | request13.setReceiverTcno("31256152521"); 119 | request13.setAmount(BigDecimal.valueOf(1000)); 120 | CreateTransferRequest request14 = new CreateTransferRequest(); 121 | request14.setCurrency("USD"); 122 | request14.setSenderUsername("Mert3"); 123 | request14.setReceiverTcno("31256152521"); 124 | request14.setAmount(BigDecimal.valueOf(1000)); 125 | 126 | List requestList = new ArrayList<>(); 127 | requestList.add(request1); 128 | requestList.add(request2); 129 | requestList.add(request3); 130 | requestList.add(request4); 131 | requestList.add(request5); 132 | requestList.add(request6); 133 | requestList.add(request7); 134 | requestList.add(request8); 135 | requestList.add(request9); 136 | requestList.add(request10); 137 | requestList.add(request11); 138 | requestList.add(request12); 139 | requestList.add(request13); 140 | requestList.add(request14); 141 | 142 | return requestList; 143 | } 144 | 145 | @Test 146 | public void createTransaction() throws Exception { 147 | 148 | boolean shouldThrowBadRequest = request.getCurrency() == null || request.getCurrency().equals("") || request.getSenderUsername() == null || request.getSenderUsername().equals("") || 149 | request.getReceiverTcno() == null || request.getReceiverTcno().equals("") || request.getReceiverTcno().length() != 11 || request.getAmount() == null || 150 | request.getAmount().signum() == 0 || request.getAmount().signum() == -1; 151 | 152 | boolean shouldThrowExceededMaxValuePerTransaction = request.getAmount() != null && request.getAmount().compareTo(BigDecimal.valueOf(20000)) == 1; // As long as we know currency is USD. 153 | 154 | Map mockedCurrencyRates = new HashMap<>(); 155 | mockedCurrencyRates.put("USD", Double.valueOf(0.666666777)); 156 | mockedCurrencyRates.put("EUR", Double.valueOf(0.1633186347)); 157 | 158 | boolean shouldThrowExceededMaxValueForDay = false; 159 | List mockedLastDayTransfers = new ArrayList<>(); 160 | if (request.getCurrency() != null && request.getCurrency().equals("EUR")) { 161 | mockedLastDayTransfers.add(new Transfer(2612L, 318L, "EUR", BigDecimal.valueOf(2500))); 162 | mockedLastDayTransfers.add(new Transfer(2612L, 318L, "EUR", BigDecimal.valueOf(8500))); 163 | mockedLastDayTransfers.add(new Transfer(2612L, 318L, "EUR", BigDecimal.valueOf(7500))); 164 | mockedLastDayTransfers.add(new Transfer(2612L, 318L, "EUR", BigDecimal.valueOf(2500))); 165 | shouldThrowExceededMaxValueForDay = true; 166 | } 167 | 168 | User mockedUserByUsername = new User("Mert", "mert123", "22512567125"); 169 | 170 | User mockedUserByTcno; 171 | if (request.getSenderUsername() != null && request.getSenderUsername().equals("Mert3")) { 172 | mockedUserByTcno = new User("Mert", "mert123", "22512567125"); 173 | shouldThrowBadRequest = true; 174 | } else { 175 | mockedUserByTcno = new User("Mert5", "mert1235", "51285625682"); 176 | } 177 | 178 | Transfer mockedTransfer = new Transfer(mockedUserByUsername.getId(), mockedUserByTcno.getId(), request.getCurrency(), request.getAmount()); 179 | 180 | Mockito.when(wealthService.getCurrencyRates()).thenReturn(mockedCurrencyRates); 181 | Mockito.when(userService.findByUserName(Mockito.anyString())).thenReturn(mockedUserByUsername); 182 | Mockito.when(transferService.findAllTransfersFrom24Hours(Mockito.any())).thenReturn(mockedLastDayTransfers); 183 | Mockito.when(userService.findByTcno(Mockito.anyString())).thenReturn(mockedUserByTcno); 184 | Mockito.when(transferService.createNewTransfer(Mockito.any())).thenReturn(mockedTransfer); 185 | 186 | String requestAsJson = new ObjectMapper().writeValueAsString(request); 187 | RequestBuilder requestBuilder = TestUtils.getPostRequestBuilder("/transfer/create", requestAsJson); 188 | 189 | ResultActions resultActions = mockMvc.perform(requestBuilder); 190 | if(shouldThrowBadRequest) { 191 | resultActions.andExpect(MockMvcResultMatchers.status().isBadRequest()); 192 | } else if (shouldThrowExceededMaxValuePerTransaction) { 193 | resultActions.andExpect(MockMvcResultMatchers.status().isUnauthorized()); 194 | } else if (shouldThrowExceededMaxValueForDay) { 195 | resultActions.andExpect(MockMvcResultMatchers.status().isUnauthorized()); 196 | } else { 197 | resultActions.andExpect(MockMvcResultMatchers.status().isOk()) 198 | .andExpect(jsonPath("$.transfer.fromUserId", equalTo(mockedTransfer.getFromUserId()))) 199 | .andExpect(jsonPath("$.transfer.toUserId", equalTo(mockedTransfer.getToUserId()))) 200 | .andExpect(jsonPath("$.transfer.currency", equalTo(mockedTransfer.getCurrency()))) 201 | .andExpect(jsonPath("$.transfer.amount", equalTo(mockedTransfer.getAmount().intValue()))); 202 | } 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.3/maven-wrapper-0.5.3.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 300 | 301 | exec "$JAVACMD" \ 302 | $MAVEN_OPTS \ 303 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 304 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 305 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 306 | --------------------------------------------------------------------------------