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

Interview Chaykin VE #45

Open
wants to merge 2 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
109 changes: 109 additions & 0 deletions interview-ChaykinVE.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
<facet type="web" name="Web">
<configuration>
<webroots />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-launcher:1.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.testcontainers:junit-jupiter:1.12.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.testcontainers:testcontainers:1.12.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.26" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.jetbrains:annotations:17.0.0" level="project" />
<orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apache.commons:commons-compress:1.19" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: javax.xml.bind:jaxb-api:2.3.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: javax.activation:javax.activation-api:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.rnorth.duct-tape:duct-tape:1.0.8" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.rnorth.visible-assertions:visible-assertions:2.1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.rnorth:tcp-unix-socket-proxy:1.0.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.kohlschutter.junixsocket:junixsocket-native-common:2.0.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.scijava:native-lib-loader:2.0.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.kohlschutter.junixsocket:junixsocket-common:2.0.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.java.dev.jna:jna-platform:4.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.java.dev.jna:jna:4.5.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.testcontainers:postgresql:1.12.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.testcontainers:jdbc:1.12.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.testcontainers:database-commons:1.12.3" level="project" />
<orderEntry type="library" name="Maven: org.postgresql:postgresql:42.2.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.11.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.11.2" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.26" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.23" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.9.9" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.9.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.9.9" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.9" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.9" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.9" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.21" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.21" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.21" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.0.17.Final" level="project" />
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.1.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.3.2.Final" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.4.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.1.5" level="project" />
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.9" level="project" />
<orderEntry type="library" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.1.6.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.1.6.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.1.6.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.11.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:2.23.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy:1.9.13" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.9.13" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.1.8.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.1.8.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.6.2" level="project" />
</component>
</module>
7 changes: 7 additions & 0 deletions resources/sql/data/accounts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create table ACCOUNTS
(
ID int primary key,
FIRST_NAME varchar,
LAST_NAME varchar,
BALANCE double
);
6 changes: 6 additions & 0 deletions resources/sql/data/select.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
select *from ACCOUNTS where ID in
(select SOURCE_ID from
(select SOURCE_ID, sum (AMOUNT) as SUMM from
(select *from TRANSFERS where TRANSFER_TIME > '2019-01-01')
group by SOURCE_ID)
where SUMM >= 1000);
8 changes: 8 additions & 0 deletions resources/sql/data/transfers.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
create table TRANSFERS
(
ID int primary key,
SOURCE_ID int references ACCOUNTS(ID),
TARGET_ID int references ACCOUNTS(ID),
AMOUNT double,
TRANSFER_TIME datetime
);
19 changes: 19 additions & 0 deletions src/com/devexperts/ApplicationRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.devexperts;

import com.devexperts.service.AccountService;
import com.devexperts.service.AccountServiceImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

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

@Bean
AccountService accountService() {
return new AccountServiceImpl();
}
}
35 changes: 35 additions & 0 deletions src/com/devexperts/account/Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.devexperts.account;

public class Account {
private final AccountKey accountKey;
private final String firstName;
private final String lastName;
private Double balance;

public Account(AccountKey accountKey, String firstName, String lastName, Double balance) {
this.accountKey = accountKey;
this.firstName = firstName;
this.lastName = lastName;
this.balance = balance;
}

public AccountKey getAccountKey() {
return accountKey;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public Double getBalance() {
return balance;
}

public void setBalance(Double balance) {
this.balance = balance;
}
}
20 changes: 20 additions & 0 deletions src/com/devexperts/account/AccountKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.devexperts.account;

/**
* Unique Account identifier
*
* <p>
* NOTE: we suspect that later {@link #accountId} is not going to be uniquely identifying an account,
* as we might add human-readable account representation and some clearing codes for partners.
* */
public class AccountKey {
private final long accountId;

private AccountKey(long accountId) {
this.accountId = accountId;
}

public static AccountKey valueOf(long accountId) {
return new AccountKey(accountId);
}
}
7 changes: 7 additions & 0 deletions src/com/devexperts/rest/AbstractAccountController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.devexperts.rest;

import org.springframework.http.ResponseEntity;

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

import com.devexperts.account.Account;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

private final AccountService accountService;

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

@PostMapping(value = "/operations/transfer")
public ResponseEntity<Void> transfer (@RequestBody long sourceId, @RequestBody long targetId,
@RequestBody double amount) {

if ((sourceId == 0) || (targetId == 0) || (amount <= 0.0)){
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}

Account sourceAccount = accountService.getAccount(sourceId);
Account targetAccount = accountService.getAccount(targetId);

if ((sourceAccount == null) || (targetAccount == null)){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

if (sourceAccount.getBalance() < amount){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

accountService.transfer(sourceAccount, targetAccount, amount);
return new ResponseEntity<>(HttpStatus.OK);
}
}
37 changes: 37 additions & 0 deletions src/com/devexperts/service/AccountService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.devexperts.service;

import com.devexperts.account.Account;

public interface AccountService {

/**
* Clears account cache
*
* */
void clear();

/**
* Creates a new account
*
* @param account account entity to add or update
* @throws IllegalArgumentException if account is already present
* */
void createAccount(Account account);

/**
* Get account from the cache
*
* @param id identification of an account to search for
* @return account associated with given id or {@code null} if account is not found in the cache
* */
Account getAccount(long id);

/**
* 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
* */
void transfer(Account source, Account target, double amount);
}
52 changes: 52 additions & 0 deletions src/com/devexperts/service/AccountServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.devexperts.service;

import com.devexperts.account.Account;
import com.devexperts.account.AccountKey;

import java.util.ArrayList;
import java.util.List;

public class AccountServiceImpl implements AccountService{

private final List<Account> accounts = new ArrayList<>();
private final Object monitor = new Object();

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

@Override
public void createAccount(Account account){
synchronized (monitor) {
accounts.add(account);
}
}

@Override
public Account getAccount(long id) {
synchronized (monitor) {
for (Account acc : accounts) {
if (acc.getAccountKey().equals(AccountKey.valueOf(id))) {
return acc;
}
}
return null;
}
}

@Override
public void transfer(Account source, Account target, double amount){
synchronized (monitor){
if (source.getBalance() < amount){
//throw new InvalidParameterException("not enough money to perform this operation");
}
else {
source.setBalance(source.getBalance() - amount);
target.setBalance(target.getBalance() + amount);
}
}
}
}