mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-17 22:24:23 +01:00
Refactors the API to run using dispatch instead of decorate
The API was never made an object since I couldn't find a way or working around the Flask decorators. By using dispatch we can get around the issues in #14 and will be able to create better mocks for the API
This commit is contained in:
66
pisa/api.py
66
pisa/api.py
@@ -14,13 +14,15 @@ from common.constants import HTTP_OK, HTTP_BAD_REQUEST, HTTP_SERVICE_UNAVAILABLE
|
|||||||
# ToDo: #5-add-async-to-api
|
# ToDo: #5-add-async-to-api
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
logger = Logger("API")
|
logger = Logger("API")
|
||||||
watcher = None
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", methods=["POST"])
|
class API:
|
||||||
def add_appointment():
|
def __init__(self, watcher):
|
||||||
|
self.watcher = watcher
|
||||||
|
|
||||||
|
def add_appointment(self):
|
||||||
"""
|
"""
|
||||||
Add appointment endpoint, it is used as the main endpoint of the Watchtower.
|
Main endpoint of the Watchtower.
|
||||||
|
|
||||||
The client sends requests (appointments) to this endpoint to request a job to the Watchtower. Requests must be json
|
The client sends requests (appointments) to this endpoint to request a job to the Watchtower. Requests must be json
|
||||||
encoded and contain an ``appointment`` field and optionally a ``signature`` and ``public_key`` fields.
|
encoded and contain an ``appointment`` field and optionally a ``signature`` and ``public_key`` fields.
|
||||||
@@ -48,7 +50,7 @@ def add_appointment():
|
|||||||
response = None
|
response = None
|
||||||
|
|
||||||
if type(appointment) == Appointment:
|
if type(appointment) == Appointment:
|
||||||
appointment_added, signature = watcher.add_appointment(appointment)
|
appointment_added, signature = self.watcher.add_appointment(appointment)
|
||||||
|
|
||||||
if appointment_added:
|
if appointment_added:
|
||||||
rcode = HTTP_OK
|
rcode = HTTP_OK
|
||||||
@@ -79,13 +81,11 @@ def add_appointment():
|
|||||||
else:
|
else:
|
||||||
return jsonify({"error": error}), rcode
|
return jsonify({"error": error}), rcode
|
||||||
|
|
||||||
|
# FIXME: THE NEXT THREE API ENDPOINTS ARE FOR TESTING AND SHOULD BE REMOVED / PROPERLY MANAGED BEFORE PRODUCTION!
|
||||||
# FIXME: THE NEXT THREE API ENDPOINTS ARE FOR TESTING AND SHOULD BE REMOVED / PROPERLY MANAGED BEFORE PRODUCTION!
|
# ToDo: #17-add-api-keys
|
||||||
# ToDo: #17-add-api-keys
|
def get_appointment(self):
|
||||||
@app.route("/get_appointment", methods=["GET"])
|
|
||||||
def get_appointment():
|
|
||||||
"""
|
"""
|
||||||
Get appointment endpoint, it gives information about a given appointment state in the Watchtower.
|
Gives information about a given appointment state in the Watchtower.
|
||||||
|
|
||||||
The information is requested by ``locator``.
|
The information is requested by ``locator``.
|
||||||
|
|
||||||
@@ -108,11 +108,11 @@ def get_appointment():
|
|||||||
response.append({"locator": locator, "status": "not_found"})
|
response.append({"locator": locator, "status": "not_found"})
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
locator_map = watcher.db_manager.load_locator_map(locator)
|
locator_map = self.watcher.db_manager.load_locator_map(locator)
|
||||||
|
|
||||||
if locator_map is not None:
|
if locator_map is not None:
|
||||||
for uuid in locator_map:
|
for uuid in locator_map:
|
||||||
appointment_data = watcher.db_manager.load_watcher_appointment(uuid)
|
appointment_data = self.watcher.db_manager.load_watcher_appointment(uuid)
|
||||||
|
|
||||||
if appointment_data is not None and appointment_data["triggered"] is False:
|
if appointment_data is not None and appointment_data["triggered"] is False:
|
||||||
# Triggered is an internal flag
|
# Triggered is an internal flag
|
||||||
@@ -121,7 +121,7 @@ def get_appointment():
|
|||||||
appointment_data["status"] = "being_watched"
|
appointment_data["status"] = "being_watched"
|
||||||
response.append(appointment_data)
|
response.append(appointment_data)
|
||||||
|
|
||||||
tracker_data = watcher.db_manager.load_responder_tracker(uuid)
|
tracker_data = self.watcher.db_manager.load_responder_tracker(uuid)
|
||||||
|
|
||||||
if tracker_data is not None:
|
if tracker_data is not None:
|
||||||
tracker_data["status"] = "dispute_responded"
|
tracker_data["status"] = "dispute_responded"
|
||||||
@@ -134,11 +134,9 @@ def get_appointment():
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def get_all_appointments(self):
|
||||||
@app.route("/get_all_appointments", methods=["GET"])
|
|
||||||
def get_all_appointments():
|
|
||||||
"""
|
"""
|
||||||
Get all appointments endpoint, it gives information about all the appointments in the Watchtower.
|
Gives information about all the appointments in the Watchtower.
|
||||||
|
|
||||||
This endpoint should only be accessible by the administrator. Requests are only allowed from localhost.
|
This endpoint should only be accessible by the administrator. Requests are only allowed from localhost.
|
||||||
|
|
||||||
@@ -153,8 +151,8 @@ def get_all_appointments():
|
|||||||
response = None
|
response = None
|
||||||
|
|
||||||
if request.remote_addr in request.host or request.remote_addr == "127.0.0.1":
|
if request.remote_addr in request.host or request.remote_addr == "127.0.0.1":
|
||||||
watcher_appointments = watcher.db_manager.load_watcher_appointments()
|
watcher_appointments = self.watcher.db_manager.load_watcher_appointments()
|
||||||
responder_trackers = watcher.db_manager.load_responder_trackers()
|
responder_trackers = self.watcher.db_manager.load_responder_trackers()
|
||||||
|
|
||||||
response = jsonify({"watcher_appointments": watcher_appointments, "responder_trackers": responder_trackers})
|
response = jsonify({"watcher_appointments": watcher_appointments, "responder_trackers": responder_trackers})
|
||||||
|
|
||||||
@@ -163,11 +161,10 @@ def get_all_appointments():
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
@app.route("/get_block_count", methods=["GET"])
|
def get_block_count():
|
||||||
def get_block_count():
|
|
||||||
"""
|
"""
|
||||||
Get block count endpoint, it provides the block height of the Watchtower.
|
Provides the block height of the Watchtower.
|
||||||
|
|
||||||
This is a testing endpoint that (most likely) will be removed in production. Its purpose is to give information to
|
This is a testing endpoint that (most likely) will be removed in production. Its purpose is to give information to
|
||||||
testers about the current block so they can define a dummy appointment without having to run a bitcoin node.
|
testers about the current block so they can define a dummy appointment without having to run a bitcoin node.
|
||||||
@@ -179,21 +176,20 @@ def get_block_count():
|
|||||||
|
|
||||||
return jsonify({"block_count": BlockProcessor.get_block_count()})
|
return jsonify({"block_count": BlockProcessor.get_block_count()})
|
||||||
|
|
||||||
|
def start(self):
|
||||||
def start_api(w):
|
|
||||||
"""
|
"""
|
||||||
This function starts the Flask server used to run the API.
|
This function starts the Flask server used to run the API. Adds all the routes to the functions listed above.
|
||||||
|
|
||||||
Args:
|
|
||||||
w (:obj:`Watcher <pisa.watcher.Watcher>`): A ``Watcher`` object.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# FIXME: Pretty ugly but I haven't found a proper way to pass it to add_appointment
|
routes = {
|
||||||
global watcher
|
"/": (self.add_appointment, ["POST"]),
|
||||||
|
"/get_appointment": (self.get_appointment, ["GET"]),
|
||||||
|
"/get_all_appointments": (self.get_all_appointments, ["GET"]),
|
||||||
|
"/get_block_count": (self.get_block_count, ["GET"]),
|
||||||
|
}
|
||||||
|
|
||||||
# ToDo: #18-separate-api-from-watcher
|
for url, params in routes.items():
|
||||||
watcher = w
|
app.add_url_rule(url, view_func=params[0], methods=params[1])
|
||||||
|
|
||||||
# Setting Flask log to ERROR only so it does not mess with out logging. Also disabling flask initial messages
|
# Setting Flask log to ERROR only so it does not mess with out logging. Also disabling flask initial messages
|
||||||
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from signal import signal, SIGINT, SIGQUIT, SIGTERM
|
|||||||
|
|
||||||
from pisa.conf import DB_PATH
|
from pisa.conf import DB_PATH
|
||||||
from common.logger import Logger
|
from common.logger import Logger
|
||||||
from pisa.api import start_api
|
from pisa.api import API
|
||||||
from pisa.watcher import Watcher
|
from pisa.watcher import Watcher
|
||||||
from pisa.builder import Builder
|
from pisa.builder import Builder
|
||||||
from pisa.conf import BTC_NETWORK, PISA_SECRET_KEY
|
from pisa.conf import BTC_NETWORK, PISA_SECRET_KEY
|
||||||
@@ -79,8 +79,8 @@ if __name__ == "__main__":
|
|||||||
watcher.appointments, watcher.locator_uuid_map = Builder.build_appointments(watcher_appointments_data)
|
watcher.appointments, watcher.locator_uuid_map = Builder.build_appointments(watcher_appointments_data)
|
||||||
watcher.block_queue = Builder.build_block_queue(missed_blocks_watcher)
|
watcher.block_queue = Builder.build_block_queue(missed_blocks_watcher)
|
||||||
|
|
||||||
# Create an instance of the Watcher and fire the API
|
# Fire the API
|
||||||
start_api(watcher)
|
API(watcher).start()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("An error occurred: {}. Shutting down".format(e))
|
logger.error("An error occurred: {}. Shutting down".format(e))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from time import sleep
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
|
||||||
from pisa.api import start_api
|
from pisa.api import API
|
||||||
from pisa.watcher import Watcher
|
from pisa.watcher import Watcher
|
||||||
from pisa.tools import bitcoin_cli
|
from pisa.tools import bitcoin_cli
|
||||||
from pisa import HOST, PORT, c_logger
|
from pisa import HOST, PORT, c_logger
|
||||||
@@ -40,7 +40,7 @@ def run_api(db_manager):
|
|||||||
)
|
)
|
||||||
watcher = Watcher(db_manager, sk_der)
|
watcher = Watcher(db_manager, sk_der)
|
||||||
|
|
||||||
api_thread = Thread(target=start_api, args=[watcher])
|
api_thread = Thread(target=API(watcher).start)
|
||||||
api_thread.daemon = True
|
api_thread.daemon = True
|
||||||
api_thread.start()
|
api_thread.start()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user