Added the "add_payee" action
This commit is contained in:
126
starling
126
starling
@@ -5,6 +5,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
@@ -13,8 +14,82 @@ base_url = 'https://api.starlingbank.com/api/v2'
|
|||||||
|
|
||||||
#-----------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------#
|
||||||
|
|
||||||
|
class Question:
|
||||||
|
|
||||||
|
def __init__(self, name, description, type, values):
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.type = type
|
||||||
|
self.values = values
|
||||||
|
self.answer = None
|
||||||
|
|
||||||
|
def ask(self):
|
||||||
|
while True:
|
||||||
|
sys.stdout.write(f'{self.description} ({self.type_description()}): ')
|
||||||
|
self.answer = sys.stdin.readline().rstrip('\r\n')
|
||||||
|
if self.valid_answer():
|
||||||
|
break
|
||||||
|
print(f'Invalid answer {self.answer!r}, please try again')
|
||||||
|
|
||||||
|
def type_description(self):
|
||||||
|
if self.type == 'string':
|
||||||
|
return f'{self.values[0]} - {self.values[1]} characters'
|
||||||
|
if self.type == 'digits':
|
||||||
|
return f'exactly {self.values} digits'
|
||||||
|
if self.type == 'enum':
|
||||||
|
return ', '.join(self.values)
|
||||||
|
else:
|
||||||
|
raise Exception(f'Unknown value type {self.type} for "{self.description}"')
|
||||||
|
|
||||||
|
def valid_answer(self):
|
||||||
|
if self.type == 'string':
|
||||||
|
size = len(self.answer)
|
||||||
|
return size >= self.values[0] and size <= self.values[1]
|
||||||
|
if self.type == 'digits':
|
||||||
|
size = len(self.answer)
|
||||||
|
return size == self.values and re.match('^\d+$', self.answer)
|
||||||
|
if self.type == 'enum':
|
||||||
|
return self.answer in self.values
|
||||||
|
else:
|
||||||
|
raise Exception(f'Unknown value type {self.type} for "{self.description}"')
|
||||||
|
|
||||||
|
class Form:
|
||||||
|
|
||||||
|
def __init__(self, topic, *questions):
|
||||||
|
self.topic = topic
|
||||||
|
self.questions = questions
|
||||||
|
|
||||||
|
def complete(self):
|
||||||
|
print(f'Please enter the details for the {self.topic}:')
|
||||||
|
print('')
|
||||||
|
for question in self.questions:
|
||||||
|
question.ask()
|
||||||
|
print('')
|
||||||
|
print(f'Please check the details for the {self.topic}:')
|
||||||
|
print('')
|
||||||
|
for question in self.questions:
|
||||||
|
print(f'{question.description}: {question.answer}')
|
||||||
|
print('')
|
||||||
|
while True:
|
||||||
|
sys.stdout.write('Are these details corrent (y/n)? ')
|
||||||
|
answer = sys.stdin.readline().rstrip('\r\n')
|
||||||
|
if answer == 'y':
|
||||||
|
break
|
||||||
|
if answer == 'n':
|
||||||
|
sys.exit(1)
|
||||||
|
return dict(map(lambda q: (q.name, q.answer), self.questions))
|
||||||
|
|
||||||
class StarlingClient:
|
class StarlingClient:
|
||||||
|
|
||||||
|
new_payee_form = Form(
|
||||||
|
'new payee',
|
||||||
|
Question('payee_type', 'Payee type', 'enum', ('BUSINESS', 'INDIVIDUAL',)),
|
||||||
|
Question('payee_name', 'Payee\'s name', 'string', (1, 255,)),
|
||||||
|
Question('sort_code', 'Sort code', 'digits', 6),
|
||||||
|
Question('account_number', 'Account number', 'digits', 8),
|
||||||
|
Question('account_description', 'Account description', 'string', (1, 255,)),
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tokens = self.read_tokens('main', 'payments')
|
self.tokens = self.read_tokens('main', 'payments')
|
||||||
|
|
||||||
@@ -34,6 +109,15 @@ class StarlingClient:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return json.loads(response.text, object_hook=lambda obj: types.SimpleNamespace(**obj))
|
return json.loads(response.text, object_hook=lambda obj: types.SimpleNamespace(**obj))
|
||||||
|
|
||||||
|
def put(self, path, data, token='main'):
|
||||||
|
url = f'{base_url}/{path}'
|
||||||
|
headers = {
|
||||||
|
'Authorization': f'Bearer {self.tokens[token]}',
|
||||||
|
}
|
||||||
|
response = requests.put(url, json=data, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return json.loads(response.text, object_hook=lambda obj: types.SimpleNamespace(**obj))
|
||||||
|
|
||||||
### Low-level API wrappers ###
|
### Low-level API wrappers ###
|
||||||
|
|
||||||
def account_holder(self):
|
def account_holder(self):
|
||||||
@@ -48,8 +132,10 @@ class StarlingClient:
|
|||||||
def balance(self, accountUid):
|
def balance(self, accountUid):
|
||||||
return self.get(f'accounts/{accountUid}/balance')
|
return self.get(f'accounts/{accountUid}/balance')
|
||||||
|
|
||||||
def payees(self):
|
def payees(self, data=None):
|
||||||
|
if data is None:
|
||||||
return self.get('payees')
|
return self.get('payees')
|
||||||
|
return self.put('payees', data)
|
||||||
|
|
||||||
### Mid-level methods to munge the data from the low-level calls ###
|
### Mid-level methods to munge the data from the low-level calls ###
|
||||||
|
|
||||||
@@ -104,6 +190,29 @@ class StarlingClient:
|
|||||||
for payee in payees:
|
for payee in payees:
|
||||||
print(f' {payee.payeeName} ({payee.payeeType})')
|
print(f' {payee.payeeName} ({payee.payeeType})')
|
||||||
|
|
||||||
|
def add_payee(self):
|
||||||
|
details = self.new_payee_form.complete()
|
||||||
|
data = {
|
||||||
|
'payeeName': details['payee_name'],
|
||||||
|
'payeeType': details['payee_type'],
|
||||||
|
'accounts': [
|
||||||
|
{
|
||||||
|
'description': details['account_description'],
|
||||||
|
'defaultAccount': True,
|
||||||
|
'countryCode': 'GB',
|
||||||
|
'accountIdentifier': details['account_number'],
|
||||||
|
'bankIdentifier': details['sort_code'],
|
||||||
|
'bankIdentifierType': 'SORT_CODE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
response = self.payees(data)
|
||||||
|
if response.success:
|
||||||
|
print(f'Successfully created payee with UID {response.payeeUid}')
|
||||||
|
else:
|
||||||
|
print(f'Failed to create payee: {response}')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------#
|
#-----------------------------------------------------------------------------#
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -111,13 +220,9 @@ parser = argparse.ArgumentParser(
|
|||||||
description='Interacts with Starling Bank\'s API to manage bank accounts.',
|
description='Interacts with Starling Bank\'s API to manage bank accounts.',
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
epilog='''recognised actions and args:
|
epilog='''recognised actions and args:
|
||||||
- show customer and account details
|
<none> - show customer and account details
|
||||||
payees - list the customer's payees
|
payees - list the customer's payees
|
||||||
contacts <domain> [<name>] - get/set contacts for domain
|
add_payee - adds a new payee
|
||||||
expire <domain> - allow domain to auto-expire
|
|
||||||
expiry - list domains with expiry info
|
|
||||||
|
|
||||||
To show the current lock state use "%(prog)s show <domain> status".
|
|
||||||
''')
|
''')
|
||||||
|
|
||||||
parser.add_argument('-t', '--test', action='store_true', default=False,
|
parser.add_argument('-t', '--test', action='store_true', default=False,
|
||||||
@@ -132,13 +237,18 @@ client = StarlingClient()
|
|||||||
action_args = args.action
|
action_args = args.action
|
||||||
if len(action_args) == 0:
|
if len(action_args) == 0:
|
||||||
client.default_action()
|
client.default_action()
|
||||||
else:
|
sys.exit(0)
|
||||||
|
|
||||||
action = action_args.pop(0)
|
action = action_args.pop(0)
|
||||||
count = len(action_args)
|
count = len(action_args)
|
||||||
if action == 'payees':
|
if action == 'payees':
|
||||||
if count > 0:
|
if count > 0:
|
||||||
parser.error(f'Too many arguments for "{action}" action')
|
parser.error(f'Too many arguments for "{action}" action')
|
||||||
client.list_payees()
|
client.list_payees()
|
||||||
|
elif action == 'add_payee':
|
||||||
|
if count > 0:
|
||||||
|
parser.error(f'Too many arguments for "{action}" action')
|
||||||
|
client.add_payee()
|
||||||
else:
|
else:
|
||||||
parser.error(f'Unknown action "{action}"')
|
parser.error(f'Unknown action "{action}"')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user