Commit 1f28f5a8 authored by Andrej Špila's avatar Andrej Špila
Browse files

Demo and functionality fixes in account manager

parent aacdc8cf
Loading
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -115,7 +115,6 @@ public class AccountRestController {
            })
    @PatchMapping("/income")
    public ResponseEntity<Void> addIncome(@Valid @RequestBody BaseTransferDto transfer) {
        // TODO currency exchange
        accountFacade.addIncome(transfer.getIban(), transfer.getAmount());
        return ResponseEntity.ok().build();
    }
@@ -128,7 +127,6 @@ public class AccountRestController {
            })
    @PatchMapping("/expense")
    public ResponseEntity<Void> subtractExpense(@Valid @RequestBody BaseTransferDto transfer) {
        // TODO currency exchange
        accountFacade.subtractExpense(transfer.getIban(), transfer.getAmount());
        return ResponseEntity.ok().build();
    }
@@ -141,7 +139,6 @@ public class AccountRestController {
            })
    @PatchMapping("/transfer")
    public ResponseEntity<Void> makeTransfer(@Valid @RequestBody TransferDto transfer) {
        // TODO currency exchange
        accountFacade.makeTransfer(transfer.getFromIban(), transfer.getToIban(), transfer.getAmount());
        return ResponseEntity.ok().build();
    }
+2 −2
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ public class CustomRestGlobalExceptionHandling {
        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
    }

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseEntity<ApiError> handleValidationExceptions(final MethodArgumentNotValidException ex, final HttpServletRequest request) {
    @ExceptionHandler({MethodArgumentNotValidException.class, IllegalArgumentException.class})
    public ResponseEntity<ApiError> handleValidationExceptions(final Exception ex, final HttpServletRequest request) {
        final ApiError apiError = new ApiError(
                LocalDateTime.now(Clock.systemUTC()),
                HttpStatus.BAD_REQUEST,
+3 −0
Original line number Diff line number Diff line
@@ -71,6 +71,9 @@ public class AccountService {
    public boolean subtractExpense(Iban iban, Double value) {
        Optional<Account> account = findByIban(iban);
        if (account.isPresent()) {
            if (account.get().getBalance() < value) {
                throw new IllegalArgumentException("Not enough balance. Current balance: " + account.get().getBalance());
            }
            account.get().removeBalance(value);
            return true;
        }
+1 −1
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ services:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin
    ports:
      - "5433:5432"
      - "5435:5432"
    networks:
      - transaction-manager-network
    volumes:
+115 −83
Original line number Diff line number Diff line
import json
import matplotlib.pyplot as plt
import pandas as pd
import requests
from requests.models import Response
import time
from http import HTTPStatus
import json
from requests.models import Response

ADS = True

@@ -14,14 +17,17 @@ ACCOUNT_MANAGER_PORT = 8081
REPORTING_MANAGER_PORT = 8082
USER_MANAGER_PORT = 8083


def create_url(port: int):
    return f'{HOST}:{port}'


TRANSACTION_MANAGER_URL = create_url(TRANSACTION_MANAGER_PORT)
ACCOUNT_MANAGER_URL = create_url(ACCOUNT_MANAGER_PORT)
REPORTING_MANAGER_URL = create_url(REPORTING_MANAGER_PORT)
USER_MANAGER_URL = create_url(USER_MANAGER_PORT)


def resolve_service_url(service_name: str):
    if service_name == 'transaction':
        return TRANSACTION_MANAGER_URL
@@ -34,12 +40,16 @@ def resolve_service_url(service_name: str):
    else:
        raise Exception('Invalid service name')


TRANSACTION_MANAGER = 'transaction'
ACCOUNT_MANAGER = 'account'
REPORTING_MANAGER = 'reporting'
USER_MANAGER = 'user'

AUTH_TOKEN = """eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJFTVBMT1lFRUBtdW5pLmN6IiwiaXNzIjoiT25saW5lQmFua2luZ1N5c3RlbSIsInNjb3BlIjoiQ1VTVE9NRVIgRU1QTE9ZRUUgdGVzdF8xIHRlc3RfMiIsImlhdCI6MTcxNjIxNTI5NX0.neQDIrJcfGJM9Swx-UrP5wArj60dtTidSxYVh7jPwB5ryIMFeC11XzC7zkwBzwniHCYQhAE6KXYRJRU3U9xOfV3dHD0m5YONFbKSOPxnib64k8PIQ7Jqp8v5dNVJjvpM0gnOMpRs1QIFB-REtG4EsIvpTmGUN2Tgl6eELG03VOzXHmxbgw8WPmeKT0rkzwHCSj8nl48x8i7uqv7i_1BYZr0wlBWwUPQU089wdekZIxv0Ah3LLbJwNQBm1fI4PMoGjQajfbywt0tBfhSrAeHOHSP0YB_t0NL0ePkzWVZR_xqVG9Vu1EVuCM8EOeNe5qfg4Oq6eyP_u-7YANt-ym_eUA"""
EMPLOYEE_TOKEN = """eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJFTVBMT1lFRUBtdW5pLmN6IiwiaXNzIjoiT25saW5lQmFua2luZ1N5c3RlbSIsInNjb3BlIjoiQ1VTVE9NRVIgRU1QTE9ZRUUgdGVzdF8xIHRlc3RfMiIsImlhdCI6MTcxNjIxNTI5NX0.neQDIrJcfGJM9Swx-UrP5wArj60dtTidSxYVh7jPwB5ryIMFeC11XzC7zkwBzwniHCYQhAE6KXYRJRU3U9xOfV3dHD0m5YONFbKSOPxnib64k8PIQ7Jqp8v5dNVJjvpM0gnOMpRs1QIFB-REtG4EsIvpTmGUN2Tgl6eELG03VOzXHmxbgw8WPmeKT0rkzwHCSj8nl48x8i7uqv7i_1BYZr0wlBWwUPQU089wdekZIxv0Ah3LLbJwNQBm1fI4PMoGjQajfbywt0tBfhSrAeHOHSP0YB_t0NL0ePkzWVZR_xqVG9Vu1EVuCM8EOeNe5qfg4Oq6eyP_u-7YANt-ym_eUA"""

customer1_token = """eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0MUBtdW5pLmN6IiwiaXNzIjoiT25saW5lQmFua2luZ1N5c3RlbSIsInNjb3BlIjoiQ1VTVE9NRVIgdGVzdF8xIiwiaWF0IjoxNzE2MjE1Mjk1fQ.a-JGzS5GWrXgk7yppoIv9Fy-PUCEO-Jd2Nmmh-eCnfmUc9ZL3jPhtv1Q5oJDyG7rdQSc5wXIQMMw4sDnkrMlXSgw2nmcBRGpwN8WfaIHkjSIZwUeXq_x_zov5xxjvMu-YeLCc2AAyD-iSwx2-a_s4O79Lpq2N5JpfEcbQnt4T-zLVPqmOuwhroKfrPdwUbAZ1NhkaJnK0VyEMpt72dkDP2hjB5tCyP5ceAfPXPZBnwlC8mFWH8dQPEnSMoeakZxmk0k2e9GBWf9NS6occQHHYPHJ2hWlt68r-yb69gFyWcyV-5rNx-65TtTB1vHduxctl2fJ0sdN07joFlR8B3tIaQ"""
customer2_token =  """eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ0ZXN0MkBtdW5pLmN6IiwiaXNzIjoiT25saW5lQmFua2luZ1N5c3RlbSIsInNjb3BlIjoiQ1VTVE9NRVIgdGVzdF8xIiwiaWF0IjoxNzE2MjE1Mjk1fQ.sdcQXAQO1QbE7jGRR0fiUitlMmJ-gbqc-hzp7l64j-a-gMD0gVfYNNRBG76tNEGLndojTDpV5DVXVu2UybCeGuMDpNcMsRw95ShkUvU5X49yXo6fc2YiV8VEW2VrR1j_h1-qEItw3LLsjnEBsA0lFfkMMpDHdfP-LrePKw3XAzb_wJJQLoe_-acfrhkEayWBooLa4zVB3VUGMl7exYeinH0s1vmCOnLMuVwQa0F2_HH879Z9xcNJYZ0V0dvWdlU01yj8mOI4UY1X2Zl93oJGm8LeTCm5w5p3VheGGI2UP-y7MT67IpQc5Pzx4lyLMImVP2gJpp4MHwcZ5_-qogNe7w"""


def is_json(myjson):
@@ -74,24 +84,36 @@ def print_status_code(response: Response):

def get(service_name: str, enpoint: str, params: dict = None, json: dict = None, token: str = None):
    url = resolve_service_url(service_name)
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or AUTH_TOKEN}'}
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or EMPLOYEE_TOKEN}'}
    response = requests.get(f'{url}/{enpoint}', headers=headers, params=params, json=json)
    time.sleep(1)
    log(response)
    return response


def post(service_name: str, enpoint: str, params: dict = None, json: dict = None, token: str = None):
    url = resolve_service_url(service_name)
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or AUTH_TOKEN}'}
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or EMPLOYEE_TOKEN}'}
    response = requests.post(f'{url}/{enpoint}', headers=headers, params=params, json=json)
    time.sleep(1)
    log(response)
    return response


def patch(service_name: str, enpoint: str, params: dict = None, json: dict = None, token: str = None):
    url = resolve_service_url(service_name)
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or EMPLOYEE_TOKEN}'}
    response = requests.patch(f'{url}/{enpoint}', headers=headers, params=params, json=json)
    time.sleep(1)
    log(response)
    return response


def delete(service_name: str, enpoint: str, params: dict = None, json: dict = None, token: str = None):
    url = resolve_service_url(service_name)
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or AUTH_TOKEN}'}
    headers = {'content-type': 'application/json', 'authorization': f'Bearer {token or EMPLOYEE_TOKEN}'}
    response = requests.delete(f'{url}/{enpoint}', headers=headers, params=params, json=json)
    time.sleep(1)
    log(response)
    return response

@@ -100,7 +122,7 @@ print("State of user DB before creating a user")
get(USER_MANAGER, 'users')

employee_data = {
    "username": "test_user",
    "username": "EMPLOYEE@muni.cz",
    "password": "secure_password",
    "firstName": "John",
    "lastName": "Doe",
@@ -117,7 +139,7 @@ employee_data = {
}

jack_customer_data = {
    "username": "jack_customer",
    "username": "test1@muni.cz",
    "password": "secure_password1",
    "firstName": "Jack",
    "lastName": "Customer",
@@ -134,7 +156,7 @@ jack_customer_data = {
}

jane_customer_data = {
    "username": "jane_customer",
    "username": "test2@muni.cz",
    "password": "secure_password2",
    "firstName": "Jane",
    "lastName": "Customer",
@@ -167,7 +189,7 @@ leaving_customer_data = {
    "isEmployee": False
}

print("Creating a users")
print("Creating users")
post(USER_MANAGER, 'users/create', employee_data)
post(USER_MANAGER, 'users/create', jack_customer_data)
post(USER_MANAGER, 'users/create', jane_customer_data)
@@ -178,93 +200,66 @@ print("State of user DB after creating a users")
get(USER_MANAGER, 'users/details')

print_divider()
print("Activating a user account")
post(USER_MANAGER, 'users/1/activate')
post(USER_MANAGER, 'users/2/activate')
post(USER_MANAGER, 'users/3/activate')

print_divider()
print("State of user DB after activating a user account")
get(USER_MANAGER, 'users/details')

print_divider()
print("Account can be also deactivated")
post(USER_MANAGER, 'users/1/deactivate')
print("Now that we have a couple users let's create some accounts for them")

print_divider()
print("State of user DB after deactivating a user account")
get(USER_MANAGER, 'users/details')

print_divider()
print("Account can be also reactivated")
response = post(USER_MANAGER, 'users/1/activate')
print_status_code(response)

print_divider()
print("Of course accounts can be deleted as well")
response = post(USER_MANAGER, 'users/4/delete')
print_status_code(response)

print_divider()
print("State of user DB after deleting a user account")
get(USER_MANAGER, 'users/details')

print_divider()
print("How about activating a deleted account?")
response = post(USER_MANAGER, 'users/4/activate')
print_status_code(response)
print("Yeah, you can't do that")

print_divider()
print("So now that we have a couple users let's create some accounts for them")

print()
print("Let's check what accounts we got in the system")
get(ACCOUNT_MANAGER, 'accounts')

print("There are couple predefined accounts in the system")
print("We can delete savings account. Who needs savings account anyway?")

if ADS:
    print_divider()
    print("ADVERT:")
    print("For any unexpected expenses, you can always take a loan from us. We have the best rates in the market.")
    print("And for saving you're better off investing with our equity funds. We have the best returns in the market.")
    print("End of advertising segment")
    print_divider()

delete(ACCOUNT_MANAGER, 'accounts/CZ331234567890000000009')

print("Accounts available after deletion")
print("Let's check if we have any accounts in the system")
get(ACCOUNT_MANAGER, 'accounts')

print("Finally let's create some accounts for our new customers")
print("As there are none, let's create some accounts for our new customers")

account1_data = {
    "name": "Crypto_withdrawals",
    "iban": "CZ4212345678900001",
    "iban": "CZ4907100000000000123457",
    "balance": 100,
    "accountType": "MAIN",
    "currency": "CZK"
    "currency": "CZK",
    "owner": "test1@muni.cz"
}

account2_data = {
    "name": "OF_deposits",
    "iban": "CZ4212345678900002",
    "iban": "CZ6508000000192000145399",
    "balance": 1_000_000,
    "accountType": "MAIN",
    "currency": "CZK"
    "currency": "CZK",
    "owner": "test2@muni.cz"
}

post(ACCOUNT_MANAGER, 'accounts', json=account1_data)
post(ACCOUNT_MANAGER, 'accounts', json=account2_data)
post(ACCOUNT_MANAGER, 'accounts', json=account1_data, token=customer1_token)
post(ACCOUNT_MANAGER, 'accounts', json=account2_data, token=customer2_token)

print("Summary of accounts after creation")
get(ACCOUNT_MANAGER, 'accounts')

# TODO transfer money between accounts
# TODO income
# TODO expense
print_divider()
print("Let's first deposit some money into test1@muni.cz owned account")
print("Before deposit calling get on the account")
get(ACCOUNT_MANAGER, 'accounts/CZ4907100000000000123457', token=customer1_token)
post(TRANSACTION_MANAGER, 'transactions/deposit', json={"toIban": "CZ4907100000000000123457", "amount": 1000, "currency": "CZK"}, token=customer1_token)
print("After deposit calling get on the account")
get(ACCOUNT_MANAGER, 'accounts/CZ4907100000000000123457', token=customer1_token)
print("Now let's withdraw some money from the account")
post(TRANSACTION_MANAGER, 'transactions/withdrawal', json={"fromIban": "CZ4907100000000000123457", "amount": 500, "currency": "CZK"}, token=customer1_token)
print("After withdrawal calling get on the account")
get(ACCOUNT_MANAGER, 'accounts/CZ4907100000000000123457', token=customer1_token)
print("And now let's transfer some money to the other account")
post(TRANSACTION_MANAGER, 'transactions/immediate', json={"senderIban": "CZ4907100000000000123457", "receiverIban": "CZ6508000000192000145399", "amount": 500, "currency": "CZK"}, token=customer1_token)
print("After transfer calling get on the accounts")
get(ACCOUNT_MANAGER, 'accounts')

print("There are also some security measures in place")
print("Let's try to withdraw more money than we have")
response = patch(ACCOUNT_MANAGER, 'accounts/expense', json={"iban": "CZ4907100000000000123457", "amount": 10000, "currency": "CZK"}, token=customer1_token)
print("after trying to withdraw more money than we have")
get(ACCOUNT_MANAGER, 'accounts/CZ4907100000000000123457', token=customer1_token)
print("Also, only jwt tokens with subject specified as the owner of the account are allowed to manipulate it")
patch(ACCOUNT_MANAGER, 'accounts/income', json={"iban": "CZ4907100000000000123457", "amount": 1000, "currency": "CZK"}, token=customer2_token)
print("Employee scopes are allowed to do any valid operation on any account")
patch(ACCOUNT_MANAGER, 'accounts/income', json={"iban": "CZ4907100000000000123457", "amount": 1000, "currency": "CZK"}, token=EMPLOYEE_TOKEN)
get(ACCOUNT_MANAGER, 'accounts/CZ4907100000000000123457', token=EMPLOYEE_TOKEN)




print_divider()

@@ -275,8 +270,45 @@ get(TRANSACTION_MANAGER, 'transactions')

print("This is of course well and good but imagine you have a lot of transactions.")
print("For these cases we have a reporting manager to give you more insights about the aggregate big picture.")
print("Reporting managers loads the transactions every 30seconds, so we need to wait at least that to be sure the data is loaded.")
time.sleep(30)

print()
get(REPORTING_MANAGER, 'total')
get(REPORTING_MANAGER, 'cash')
get(REPORTING_MANAGER, 'average')
 No newline at end of file
post(REPORTING_MANAGER, 'reporting/total', json={"accountNumbers": ["CZ4907100000000000123457"]})
post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ4907100000000000123457"]})
post(REPORTING_MANAGER, 'reporting/average', json={"accountNumbers": ["CZ4907100000000000123457"]})

print_divider()
print("Now let's add some transactions directly into reporting manager db to showcase its use case")
get(REPORTING_MANAGER, 'reporting/test')

response_1 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ2850514863768494113256"]})
response_2 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ6050516263453833731579"]})
response_3 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ1750512527682967891453"]})
response_4 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ2550515548468511346336"]})
response_5 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ3250517347693611456594"]})
response_6 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ8450516554635931338193"]})
response_7 = post(REPORTING_MANAGER, 'reporting/cash', json={"accountNumbers": ["CZ1150515754779739431496"]})

accounts = ['CZ2850514863768494113256', 'CZ6050516263453833731579', 'CZ1750512527682967891453', 'CZ2550515548468511346336', 'CZ3250517347693611456594', 'CZ8450516554635931338193', 'CZ1150515754779739431496']
responses = [response_1, response_2, response_3, response_4, response_5, response_6, response_7]
data = []
for i, response in enumerate(responses):
    myJson = json.loads(response.text)
    data.append({
        'debit': abs(myJson['debit']),
        'balance': abs(myJson['balance']),
        'credit': abs(myJson['credit']),
        'account': '_' + accounts[i][-4:]
    })
df = pd.DataFrame(data)

# Plot
colors = ['green', 'red']
df.plot(x='account', y=['debit', 'credit'], kind='bar', color=colors)
plt.title('Debit and Credit values for each account')
plt.xlabel('Account Number')
plt.ylabel('Value in CZK')
plt.xticks(rotation=0)
plt.savefig('report.png')
plt.show()
Loading