· 5 years ago · Nov 26, 2020, 11:40 AM
1import json
2import logging
3import time
4
5import requests
6from django.conf import settings
7from olivia.wealthblock.exceptions import (
8 InvalidWealthblockUrl,
9 UnexpectedWealthblockResponse,
10 WealthblockAccountNotFound,
11)
12from rest_framework import status
13
14
15class WealthblockClient:
16 """Wealthblock API client.
17
18 Provides basic autentication methods,
19 API urls mapping and abstract http methods.
20
21 Attributes:
22 BEARER_TOKEN_LIFETIME (int): lifetime of bearer token in seconds.
23 URLS (list): urls mapping of Wealthblock API.
24 """
25
26 BEARER_TOKEN_LIFETIME = 3600
27 URLS = {
28 "auth": {"post": "platform/auth"},
29 "accounts": {"getlist": "account/search"},
30 }
31
32 def __init__(self):
33 """Initiates basic class configuration.
34
35 Attributes:
36 api_key (str): API key of Wealthblock API.
37 base_url (str): base url of Wealthblock API.
38 basic_headers (dict): request headers should be
39 included to interact with Wealthblock API.
40 bearer_token (dict): Wealthblock bearer token data.
41 """
42
43 self.api_key = settings.WEALTHBLOCK_API_KEY
44 self.base_url = settings.BASE_URL
45 self.basic_headers = {
46 "Content-Type": "application/json",
47 "Client-ID": settings.WEALTHBLOCK_API_CLIENT_ID,
48 }
49 self.bearer_token = {"expiring_unixtime": 0, "token": None}
50
51 def make_authhorized_request(
52 self, url, method, data=None, params=None, expected_status_codes=None
53 ):
54 "Performs authhorized request to Wealthblock API."
55
56 if not self.is_authorized:
57 self.authorize()
58 return self._make_request(url, method, data, params, expected_status_codes)
59
60 def raise_error(self, error):
61 "Creates log entry and raises error."
62
63 logger = logging.getLogger("olivia_wealthblock")
64 logger.error(error.message)
65 raise error
66
67 @property
68 def headers(self):
69 """Returns headers should be included to make request
70 to Wealthblock API.
71 """
72
73 if self.is_authorized:
74 return {
75 "Authorization": f'Bearer {self.bearer_token["token"]}',
76 **self.basic_headers,
77 }
78 return self.basic_headers
79
80 @property
81 def is_authorized(self):
82 "Returns bearer token is valid."
83
84 return self.bearer_token["expiring_unixtime"] > time.time()
85
86 def authorize(self):
87 "Makes request and gets bearer token."
88
89 url = self._get_app_url("auth", "post")
90 response = self._make_request(url, "post", data={"apiKey": self.api_key})
91 self.bearer_token = {
92 "expiring_unixtime": time.time() + self.BEARER_TOKEN_LIFETIME * 0.95,
93 "token": response,
94 }
95
96 def _make_request(
97 self, url, method, data=None, params=None, expected_status_codes=None
98 ):
99 """Performs http request.
100
101 Raises error in case server returned
102 not expected http status code.
103
104 Args:
105 url (str): url should be requested.
106 method (str): name of http request method.
107 data (dict): body of request
108 params (dict): params of GET request.
109 expected_status_codes (iterable): expected http status codes.
110 """
111
112 expected_codes = expected_status_codes or [status.HTTP_200_OK]
113
114 response = getattr(requests, method)(
115 url, json=data or {}, params=params or {}, headers=self.headers
116 )
117 if response.status_code not in expected_codes:
118 self.raise_error(
119 UnexpectedWealthblockResponse(url, method, data, self.headers, response)
120 )
121 return self._get_response_content(response, url, method, data)
122
123 def _get_response_content(self, response, url, method, data):
124 "Returns Wealthblock API response content."
125
126 try:
127 return json.loads(response.content).get("data", {})
128 except json.JSONDecodeError:
129 self.raise_error(
130 UnexpectedWealthblockResponse(url, method, data, self.headers, response)
131 )
132
133 def _get_app_url(self, app_name, method_name):
134 """Returns Wealthblock API url.
135
136 Args:
137 app_name (str): name of requested API endpoint.
138 method_name (str): name of requested API method.
139 """
140
141 app_url = self.URLS.get(app_name, {}).get(method_name)
142 if not app_url:
143 self.raise_error(InvalidWealthblockUrl(app_name, method_name, self.URLS))
144 return self.base_url + app_url
145
146
147class WealthblockAPI(WealthblockClient):
148 "Client wrapper for Wealthblock REST API."
149
150 def get_val_user_by_email(self, email):
151 """Searches for user data using email.
152
153 Args:
154 email (str): email of Wealthblock user.
155
156 Returns:
157 dict: Wealthblock user data.
158 example:
159 {
160 'id': '5fad91e3cbdcf314e7a13d07',
161 'accountNumber': '4463585125',
162 'defaultRoleType': 3,
163 'parentAccountId': '5fad91e3cbdcf314e7a13d06',
164 'profile': {
165 'firstName': 'John',
166 'lastName': 'Doe',
167 'middleName': 'Alekseyevich',
168 'title': 'Test',
169 'birthDate': '1970-01-01T00:00:00.000Z',
170 'companyName': "The company name",
171 'street': 'The street',
172 'city': 'The city',
173 'street2': 'The street2',
174 'state': 'The state',
175 'zip': '12345',
176 'phone': '+380661234567',
177 'email': 'foo@bar.com'
178 },
179 'name': "The company name",
180 '_ts': 1605210595180,
181 'notApproved': None,
182 '_p': [797]
183 }
184 """
185
186 url = self._get_app_url("accounts", "getlist")
187
188 response = self.make_authhorized_request(
189 url, "get", params={"page": 1, "pageSize": 1, "search": email}
190 )
191
192 for user_data in response.get("results", []):
193 if user_data["profile"]["email"].lower() == email.lower():
194 return user_data
195
196 self.raise_error(WealthblockAccountNotFound(email, response))
197