Skip to content
This repository has been archived by the owner on Jan 17, 2022. It is now read-only.

Kiril petrov interview #43

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions src/main/java/com/devexperts/ApplicationRunner.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.devexperts;

import com.devexperts.service.AccountService;
import com.devexperts.service.AccountServiceImpl;
import com.devexperts.service.impl.AccountServiceImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
Expand All @@ -12,8 +12,4 @@ public static void main(String[] args) {
SpringApplication.run(ApplicationRunner.class, args);
}

@Bean
AccountService accountService() {
return new AccountServiceImpl();
}
}
1 change: 1 addition & 0 deletions src/main/java/com/devexperts/account/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ public Double getBalance() {
public void setBalance(Double balance) {
this.balance = balance;
}

}
44 changes: 44 additions & 0 deletions src/main/java/com/devexperts/dto/ErrorDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.devexperts.dto;

import org.springframework.http.HttpStatus;

import java.time.LocalDateTime;

public class ErrorDto {

private String message;

private HttpStatus status;

private LocalDateTime time;

public ErrorDto(String message, HttpStatus status, LocalDateTime time) {
this.message = message;
this.status = status;
this.time = time;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public HttpStatus getStatus() {
return status;
}

public void setStatus(HttpStatus status) {
this.status = status;
}

public LocalDateTime getTime() {
return time;
}

public void setTime(LocalDateTime time) {
this.time = time;
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/devexperts/dto/TransferMoneyDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.devexperts.dto;

import javax.validation.constraints.NotNull;

public class TransferMoneyDto {

@NotNull
private Long sourceId;
@NotNull
private Long targetId;
private double amount;

public TransferMoneyDto(Long sourceId, Long targetId, double amount) {
this.sourceId = sourceId;
this.targetId = targetId;
this.amount = amount;
}

public Long getSourceId() {
return sourceId;
}

public void setSourceId(Long sourceId) {
this.sourceId = sourceId;
}

public Long getTargetId() {
return targetId;
}

public void setTargetId(Long targetId) {
this.targetId = targetId;
}

public double getAmount() {
return amount;
}

public void setAmount(double amount) {
this.amount = amount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.devexperts.exception;

public class BadRequestException extends RuntimeException {

public BadRequestException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.devexperts.exception;

public class InsufficentFundsException extends RuntimeException {

public InsufficentFundsException(String msg) {
super(msg);
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/devexperts/exception/NotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.devexperts.exception;

public class NotFoundException extends RuntimeException {

public NotFoundException(String msg) {
super(msg);
}

}
57 changes: 57 additions & 0 deletions src/main/java/com/devexperts/handler/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.devexperts.handler;

import com.devexperts.dto.ErrorDto;
import com.devexperts.exception.BadRequestException;
import com.devexperts.exception.InsufficentFundsException;
import com.devexperts.exception.NotFoundException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(BadRequestException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleBadRequests(BadRequestException exception) {
return convertExceptionToErrorDto(HttpStatus.BAD_REQUEST, exception.getMessage());
}

@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorDto handleBadRequests(NotFoundException exception) {
return convertExceptionToErrorDto(HttpStatus.NOT_FOUND, exception.getMessage());
}

@ExceptionHandler(InsufficentFundsException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorDto handleBadRequests(InsufficentFundsException exception) {
return convertExceptionToErrorDto(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
final Map<String, String> errorMessages = exception.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(FieldError::getField, DefaultMessageSourceResolvable::getDefaultMessage));
return convertExceptionToErrorDto(HttpStatus.BAD_REQUEST, errorMessages.toString());
}

@ExceptionHandler(NumberFormatException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleMethodArgumentNotValidException(NumberFormatException exception) {
return convertExceptionToErrorDto(HttpStatus.BAD_REQUEST, exception.getMessage());
}

private static ErrorDto convertExceptionToErrorDto(final HttpStatus status, final String message) {
return new ErrorDto(message, status, LocalDateTime.now());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.devexperts.rest;

import com.devexperts.dto.TransferMoneyDto;
import org.springframework.http.ResponseEntity;

public abstract class AbstractAccountController {
abstract ResponseEntity<Void> transfer(long sourceId, long targetId, double amount);
abstract ResponseEntity<Void> transfer(TransferMoneyDto transferMoneyDto);
}
22 changes: 20 additions & 2 deletions src/main/java/com/devexperts/rest/AccountController.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
package com.devexperts.rest;

import com.devexperts.dto.TransferMoneyDto;
import com.devexperts.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/api")
public class AccountController extends AbstractAccountController {

public ResponseEntity<Void> transfer(long sourceId, long targetId, double amount) {
return null;
private final AccountService service;

@Autowired
public AccountController(AccountService service) {
this.service = service;
}

@PostMapping("operations/transfer")
public ResponseEntity<Void> transfer(@Valid TransferMoneyDto transferMoneyDto) {
service.transfer(transferMoneyDto);
return new ResponseEntity<>(HttpStatus.OK);
}

}
7 changes: 3 additions & 4 deletions src/main/java/com/devexperts/service/AccountService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.devexperts.service;

import com.devexperts.account.Account;
import com.devexperts.dto.TransferMoneyDto;

public interface AccountService {

Expand Down Expand Up @@ -29,9 +30,7 @@ public interface AccountService {
/**
* Transfers given amount of money from source account to target account
*
* @param source account to transfer money from
* @param target account to transfer money to
* @param amount dollar amount to transfer
* @param dto the ids of both accounts and the amount to be transferred
* */
void transfer(Account source, Account target, double amount);
void transfer(TransferMoneyDto dto);
}
37 changes: 0 additions & 37 deletions src/main/java/com/devexperts/service/AccountServiceImpl.java

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/java/com/devexperts/service/impl/AccountServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.devexperts.service.impl;

import com.devexperts.account.Account;
import com.devexperts.account.AccountKey;
import com.devexperts.dto.TransferMoneyDto;
import com.devexperts.exception.BadRequestException;
import com.devexperts.exception.NotFoundException;
import com.devexperts.service.AccountService;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class AccountServiceImpl implements AccountService {

private final Map<AccountKey, Account> accounts = new HashMap<>();

@Override
public void clear() {
accounts.clear();
}

@Override
public void createAccount(Account account) {
accounts.put(account.getAccountKey(), account);
}

@Override
public Account getAccount(long id) {
return accounts.get(AccountKey.valueOf(id));
}

@Override
public synchronized void transfer(TransferMoneyDto dto) {
Account source = accounts.get(AccountKey.valueOf(dto.getSourceId()));
Account target = accounts.get(AccountKey.valueOf(dto.getTargetId()));
transfer(source, target, dto.getAmount());
}

public void transfer(Account source, Account target, double amount) {
if(source == null) {
throw new NotFoundException("The source account doesnt exist");
}
if(target == null) {
throw new NotFoundException("The target account doesnt exist");
}
if(source.equals(target)) {
throw new BadRequestException("Its pointless to transfer money between the same account");
}
if(amount < 0) {
throw new BadRequestException("The amount can't be a negative value");
}
if(source.getBalance() < amount) {
throw new BadRequestException("The source account doesnt have sufficient amount");
}
source.setBalance(source.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
1 change: 1 addition & 0 deletions src/main/resources/sql/data/accounts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE TABLE IF NOT EXISTS accounts (ID int, FIRST_NAME varchar(255), LAST_NAME varchar(255), BALANCE DECIMAL);
1 change: 1 addition & 0 deletions src/main/resources/sql/data/select.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM accounts AS ACC INNER JOIN transfers AS TR on ACC.ID = TR.SOURCE_ID WHERE TRANSFER_TIME >= '2019-01-01' GROUP BY ACC.ID HAVING SUM(TR.AMOUNT) > 1000
1 change: 1 addition & 0 deletions src/main/resources/sql/data/transfers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE TABLE IF NOT EXISTS transfers (ID int, SOURCE_ID int, TARGET_ID int, AMOUNT DECIMAL, TRANSFER_TIME TIMESTAMP);