-
Notifications
You must be signed in to change notification settings - Fork 29
/
banking-client.go
110 lines (89 loc) · 3.03 KB
/
banking-client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package app
// This code simulates a client for a hypothetical banking service.
// It supports both withdrawals and deposits, and generates a
// pseudorandom transaction ID for each request.
//
// Tip: You can modify these functions to introduce delays or errors, allowing
// you to experiment with failures and timeouts.
import (
"errors"
"math/rand"
)
type account struct {
AccountNumber string
Balance int64
}
type bank struct {
Accounts []account
}
func (b bank) findAccount(accountNumber string) (account, error) {
for _, v := range b.Accounts {
if v.AccountNumber == accountNumber {
return v, nil
}
}
return account{}, errors.New("account not found")
}
// InsufficientFundsError is raised when the account doesn't have enough money.
type InsufficientFundsError struct{}
func (m *InsufficientFundsError) Error() string {
return "Insufficient Funds"
}
// InvalidAccountError is raised when the account number is invalid
type InvalidAccountError struct{}
func (m *InvalidAccountError) Error() string {
return "Account number supplied is invalid"
}
// our mock bank
var mockBank = &bank{
Accounts: []account{
{AccountNumber: "85-150", Balance: 2000},
{AccountNumber: "43-812", Balance: 0},
},
}
// BankingService mocks interaction with a bank API. It supports withdrawals
// and deposits
type BankingService struct {
// the hostname is to make it more realistic. This code does not
// actually make any network calls.
Hostname string
}
// Withdraw simulates a Withdrawal from a bank.
// Accepts the account number (string), amount (int), and a reference ID (string)
// for idempotent transaction tracking.
// Returns a transaction id when successful
// Returns various errors based on amount and account number.
func (client BankingService) Withdraw(accountNumber string, amount int, referenceID string) (string, error) {
acct, err := mockBank.findAccount(accountNumber)
if err != nil {
return "", &InvalidAccountError{}
}
if amount > int(acct.Balance) {
return "", &InsufficientFundsError{}
}
return generateTransactionID("W", 10), nil
}
// Deposit simulates a Deposit into a bank.
// Accepts the account number (string), amount (int), and a reference ID (string)
// for idempotent transaction tracking.
// Returns a transaction id when successful
// Returns InvalidAccountError if the account is invalid
func (client BankingService) Deposit(accountNumber string, amount int, referenceID string) (string, error) {
_, err := mockBank.findAccount(accountNumber)
if err != nil {
return "", &InvalidAccountError{}
}
return generateTransactionID("D", 10), nil
}
// DepositThatFails simulates an unknown error.
func (client BankingService) DepositThatFails(accountNumber string, amount int, referenceID string) (string, error) {
return "", errors.New("This deposit has failed.")
}
func generateTransactionID(prefix string, length int) string {
randChars := make([]byte, length)
for i := range randChars {
allowedChars := "0123456789"
randChars[i] = allowedChars[rand.Intn(len(allowedChars))]
}
return prefix + string(randChars)
}