Implement serde for JSON deserialisation

This commit is contained in:
Mark Veidemanis 2022-03-25 18:04:17 +00:00
parent a69a585c71
commit b3a39401b5
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
2 changed files with 73 additions and 46 deletions

View File

@ -5,6 +5,8 @@ from twisted.internet.task import LoopingCall
import requests import requests
from simplejson.errors import JSONDecodeError from simplejson.errors import JSONDecodeError
from json import dumps, loads from json import dumps, loads
from lib.serde.nordigen import TXRoot, AccessToken, Institutions, Agreement, Requisitions, AccountDetails
from serde import ValidationError
# Project imports # Project imports
from settings import settings from settings import settings
@ -57,15 +59,13 @@ class Nordigen(util.Base):
path = f"{settings.Nordigen.Base}/token/new/" path = f"{settings.Nordigen.Base}/token/new/"
r = requests.post(path, headers=headers, data=dumps(data)) r = requests.post(path, headers=headers, data=dumps(data))
try: try:
parsed = r.json() obj = AccessToken.from_json(r.content)
except JSONDecodeError: except ValidationError as err:
self.log.error(f"Error parsing access token response: {r.content}") self.log.error(f"Validation error: {err}")
return False return
if "access" in parsed: parsed = obj.to_dict()
self.token = parsed["access"] self.token = parsed["access"]
self.log.info("Refreshed access token") self.log.info("Refreshed access token")
else:
self.log.error(f"Access token not in response: {parsed}")
if not self.authed: if not self.authed:
self.__authed__() self.__authed__()
self.authed = True self.authed = True
@ -84,10 +84,17 @@ class Nordigen(util.Base):
path = f"{settings.Nordigen.Base}/institutions/?country={country}" path = f"{settings.Nordigen.Base}/institutions/?country={country}"
r = requests.get(path, headers=headers) r = requests.get(path, headers=headers)
try: try:
parsed = r.json() parsed_pre = r.json()
except JSONDecodeError: except JSONDecodeError:
self.log.error(f"Error parsing institutions response: {r.content}") self.log.error(f"Error parsing institutions response: {r.content}")
return False return False
parsed = {"institutions": parsed_pre}
try:
obj = Institutions.from_dict(parsed)
except ValidationError as err:
self.log.error(f"Validation error: {err}")
return
parsed = obj.to_dict()["institutions"]
new_list = [] new_list = []
if filter_name: if filter_name:
for i in parsed: for i in parsed:
@ -96,21 +103,6 @@ class Nordigen(util.Base):
return new_list return new_list
return parsed return parsed
def create_agreement(self, institution_id):
"""Create an agreement to access an institution.
:param institution_id: ID of the institution
"""
headers = {"accept": "application/json", "Authorization": f"Bearer {self.token}"}
path = f"{settings.Nordigen.Base}/agreements/enduser"
data = {"institution_id": institution_id}
r = requests.post(path, headers=headers, data=dumps(data))
try:
parsed = r.json()
except JSONDecodeError:
self.log.error(f"Error parsing agreement response: {r.content}")
return False
return parsed
def build_link(self, institution_id): def build_link(self, institution_id):
"""Create a link to access an institution. """Create a link to access an institution.
:param institution_id: ID of the institution :param institution_id: ID of the institution
@ -120,10 +112,11 @@ class Nordigen(util.Base):
data = {"institution_id": institution_id, "redirect": settings.Nordigen.CallbackURL} data = {"institution_id": institution_id, "redirect": settings.Nordigen.CallbackURL}
r = requests.post(path, headers=headers, data=data) r = requests.post(path, headers=headers, data=data)
try: try:
parsed = r.json() obj = Agreement.from_json(r.content)
except JSONDecodeError: except ValidationError as err:
self.log.error(f"Error parsing link response: {r.content}") self.log.error(f"Validation error: {err}")
return False return
parsed = obj.to_dict()
if "link" in parsed: if "link" in parsed:
return parsed["link"] return parsed["link"]
return False return False
@ -150,14 +143,16 @@ class Nordigen(util.Base):
path = f"{settings.Nordigen.Base}/requisitions" path = f"{settings.Nordigen.Base}/requisitions"
r = requests.get(path, headers=headers) r = requests.get(path, headers=headers)
try: try:
parsed = r.json() obj = Requisitions.from_json(r.content)
except JSONDecodeError: except ValidationError as err:
self.log.error(f"Error parsing requisitions response: {r.content}") self.log.error(f"Validation error: {err}")
return False return
parsed = obj.to_dict()
if "results" in parsed: if "results" in parsed:
return parsed["results"] return parsed["results"]
else: else:
self.log.error(f"Results not in requisitions response: {parsed}") self.log.error(f"Results not in requisitions response: {parsed}")
return False return False
def get_accounts(self, requisition): def get_accounts(self, requisition):
@ -168,10 +163,11 @@ class Nordigen(util.Base):
path = f"{settings.Nordigen.Base}/requisitions/{requisition}/" path = f"{settings.Nordigen.Base}/requisitions/{requisition}/"
r = requests.get(path, headers=headers) r = requests.get(path, headers=headers)
try: try:
parsed = r.json() obj = Agreement.from_json(r.content)
except JSONDecodeError: except ValidationError as err:
self.log.error(f"Error parsing accounts response: {r.content}") self.log.error(f"Validation error: {err}")
return False return
parsed = obj.to_dict()
if "accounts" in parsed: if "accounts" in parsed:
return parsed["accounts"] return parsed["accounts"]
return False return False
@ -184,14 +180,11 @@ class Nordigen(util.Base):
path = f"{settings.Nordigen.Base}/accounts/{account_id}/details/" path = f"{settings.Nordigen.Base}/accounts/{account_id}/details/"
r = requests.get(path, headers=headers) r = requests.get(path, headers=headers)
try: try:
parsed = r.json() obj = AccountDetails.from_json(r.content)
except JSONDecodeError: except ValidationError as err:
self.log.error(f"Error parsing account response: {r.content}") self.log.error(f"Validation error: {err}")
return False return
# if "accounts" in parsed: parsed = obj.to_dict()["account"]
# return parsed["accounts"]
# return False
parsed = parsed["account"]
if "bban" in parsed and parsed["currency"] == "GBP": if "bban" in parsed and parsed["currency"] == "GBP":
sort_code = parsed["bban"][0:6] sort_code = parsed["bban"][0:6]
account_number = parsed["bban"][6:] account_number = parsed["bban"][6:]
@ -252,3 +245,16 @@ class Nordigen(util.Base):
else: else:
to_return[req["institution_id"]] = [account_info] to_return[req["institution_id"]] = [account_info]
return to_return return to_return
def get_transactions(self, account_id):
"""
Get all transactions for an account.
:param account_id: account to fetch transactions for
:return: list of transactions
:rtype: dict
"""
headers = {"accept": "application/json", "Authorization": f"Bearer {self.token}"}
path = f"{settings.Nordigen.Base}/accounts/{account_id}/transactions/"
r = requests.get(path, headers=headers)
obj = TXRoot.from_json(r.content)
return obj.to_dict()["transactions"]["booked"]

View File

@ -541,7 +541,7 @@ class IRCCommands(object):
class transactions(object): class transactions(object):
name = "transactions" name = "transactions"
authed = True authed = True
helptext = "Get a list of transactions. Usage: transactions <account> <account_id>" helptext = "Get a list of transactions from TrueLayer. Usage: transactions <account> <account_id>"
@staticmethod @staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux): def run(cmd, spl, length, authed, msg, agora, tx, ux):
@ -559,6 +559,27 @@ class IRCCommands(object):
description = transaction["description"] description = transaction["description"]
msg(f"{timestamp} {txid} {ptxid} {txtype} {amount}{currency} {description}") msg(f"{timestamp} {txid} {ptxid} {txtype} {amount}{currency} {description}")
class ntransactions(object):
name = "ntransactions"
authed = True
helptext = "Get a list of transactions from Nordigen. Usage: ntransactions <account_id>"
@staticmethod
def run(cmd, spl, length, authed, msg, agora, tx, ux):
if length == 2:
account_id = spl[1]
transactions = tx.sinks.nordigen.get_transactions(account_id)
for transaction in transactions:
msg(dumps(transaction))
# txid = transaction["transaction_id"]
# ptxid = transaction["meta"]["provider_transaction_id"]
# txtype = transaction["transaction_type"]
# timestamp = transaction["timestamp"]
# amount = transaction["amount"]
# currency = transaction["currency"]
# description = transaction["description"]
# msg(f"{timestamp} {txid} {ptxid} {txtype} {amount}{currency} {description}")
class mapaccount(object): class mapaccount(object):
name = "mapaccount" name = "mapaccount"
authed = True authed = True