-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathwebserver.py
126 lines (94 loc) · 3.33 KB
/
webserver.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!python
"""
Wraps a very simple webserver designed only for serving up the HTML to
run the Plaid Link service and capture the response JSON from the Plaid Link
API. This API must be run in the web browser, so this does the bare minimum
to accomplish this.
https://plaid.com/docs/link/
"""
import json
import logging
import mimetypes
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Dict
log = logging.getLogger(__name__)
class DataStore:
def __init__(self, config_json: Dict):
self.config_json: Dict = config_json
self.plaid_response: Dict = None
class PlaidLinkHTTPServer(BaseHTTPRequestHandler):
def __init__(self, data_store: DataStore, *args, **kwargs):
self.data_store = data_store
super().__init__( *args, **kwargs)
def serve_file(self, file_path: str):
mimetype = mimetypes.guess_type(file_path)
self.send_response(200)
self.send_header('Content-type', mimetype[0])
self.end_headers()
with open(file_path, "r") as f:
html = f.read()
html = html.replace(
"{{CONFIG_JSON}}",
json.dumps(self.data_store.config_json)
)
self.wfile.write(html.encode('utf-8'))
self.wfile.flush()
def log_request(self, code=None, size=None) -> None:
pass
def send_404(self):
self.send_response(404)
self.send_header('Content-type', "text/plain")
self.end_headers()
self.wfile.write(b"not found")
self.wfile.flush()
def do_POST(self):
path = self.path.split("?")[0]
if path == "/api/success":
cl = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(cl)
self.data_store.plaid_response = json.loads(body)
self.server.shutdown()
self.server.server_close()
return
else:
self.send_404()
def do_GET(self):
path = self.path.split("?")[0]
if path == "/link.html":
self.serve_file("html/link.html")
return
else:
self.send_404()
def serve(env: str, clientName: str, token: str, pageTitle: str, accountName: str, type: str) -> Dict:
"""
Starts a webserver and serves the html/link.html file with the
specified configuration.
Host and port will be 127.0.0.1:4583
Returns the JSON returned by the Plaid Link API when the user has successfully
finished the authorization flow.
"""
config_json = dict(
env=env,
clientName=clientName,
token=token,
pageTitle=pageTitle,
accountName=accountName,
type=type
)
ds: DataStore = DataStore(config_json)
def make_handler(*args, **kwargs):
return PlaidLinkHTTPServer(ds, *args, **kwargs)
with ThreadingHTTPServer(('127.0.0.1', 4583), make_handler) as httpd:
host, port = httpd.socket.getsockname()
print('Open the following page in your browser to continue:')
print(f' http://{host}:{port}/link.html')
try:
# well, until the API to shutdown is called
httpd.serve_forever()
except KeyboardInterrupt:
print("Keyboard interrupt received, exiting.")
sys.exit(0)
return ds.plaid_response
if __name__ == '__main__':
serve({})