From b6b95a9821f9405a31b06681f648da35fba07b0f Mon Sep 17 00:00:00 2001 From: Turtle Date: Sat, 28 Mar 2020 18:48:45 -0400 Subject: [PATCH] Add get_all_appointments cli command --- cli/README.md | 14 +++++++++-- cli/help.py | 12 +++++++++ cli/teos_cli.py | 45 ++++++++++++++++++++++++++++++++-- test/cli/unit/test_teos_cli.py | 40 ++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/cli/README.md b/cli/README.md index 61b9082..a91d304 100644 --- a/cli/README.md +++ b/cli/README.md @@ -104,8 +104,18 @@ if `-f, --file` **is** specified, then the command expects a path to a json file python teos_cli.py get_appointment +### get_all_appointments + +This command is used to get information about all the appointments stored in a Eye of Satoshi tower. + +**Responses** + +This command returns all appointments stored in the watchtower. More precisely, it returns all the "response_trackers" and "watchtower_appointments" in a dictionary. + +#### Usage + + python teos_cli.py get_all_appointments - ### help Shows the list of commands or help about how to run a specific command. @@ -161,4 +171,4 @@ python teos_cli.py -s https://teosmainnet.pisa.watch add_appointment -f dummy_ap You can also change the config file to avoid specifying the server every time: -`TEOS_SERVER = "https://teosmainnet.pisa.watch"` \ No newline at end of file +`TEOS_SERVER = "https://teosmainnet.pisa.watch"` diff --git a/cli/help.py b/cli/help.py index 4ecf172..00ba042 100644 --- a/cli/help.py +++ b/cli/help.py @@ -6,6 +6,7 @@ def show_usage(): "\n\tregister \tRegisters your user public key with the tower." "\n\tadd_appointment \tRegisters a json formatted appointment with the tower." "\n\tget_appointment \tGets json formatted data about an appointment from the tower." + "\n\tget_all_appointments \tGets information about all appointments stored in the tower." "\n\thelp \t\t\tShows a list of commands or help for a specific command." "\n\nGLOBAL OPTIONS:" "\n\t-s, --server \tAPI server where to send the requests. Defaults to 'localhost' (modifiable in conf file)." @@ -51,3 +52,14 @@ def help_get_appointment(): "\n\nDESCRIPTION:" "\n\n\tGets json formatted data about an appointment from the tower.\n" ) + + +def help_get_all_appointments(): + return ( + "NAME:" + "\tpython teos_cli get_all_appointments - Gets information about all appointments stored in the tower." + "\n\nUSAGE:" + "\tpython teos_cli get_all_appointments" + "\n\nDESCRIPTION:" + "\n\n\tGets information about all appointments stored in the tower.\n" + ) diff --git a/cli/teos_cli.py b/cli/teos_cli.py index b53f8c3..c773dcb 100644 --- a/cli/teos_cli.py +++ b/cli/teos_cli.py @@ -10,7 +10,7 @@ from getopt import getopt, GetoptError from requests import ConnectTimeout, ConnectionError from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL -from cli.help import show_usage, help_add_appointment, help_get_appointment, help_register +from cli.help import show_usage, help_add_appointment, help_get_appointment, help_register, help_get_all_appointments from cli import DEFAULT_CONF, DATA_DIR, CONF_FILE_NAME, LOG_PREFIX import common.cryptographer @@ -175,6 +175,39 @@ def get_appointment(locator, cli_sk, teos_pk, teos_url): return response_json +def get_all_appointments(teos_url): + """ + Gets information about all appointments stored in the tower, if the user requesting the data is an administrator. + + Args: + get_all_appointments_endpoint (:obj:`str`): the teos endpoint from which all appointments can be retrieved. + + Returns: + :obj:`dict` a dictionary containing all the appointments stored by the Responder and Watcher if the tower + responds. + """ + + get_all_appointments_endpoint = "{}/get_all_appointments".format(teos_url) + + try: + response = requests.get(url=get_all_appointments_endpoint, timeout=5) + + if response.status_code != constants.HTTP_OK: + logger.error("The server returned a 'not found' error") + return None + + response_json = json.dumps(response.json(), indent=4, sort_keys=True) + return response_json + + except ConnectionError: + logger.error("Can't connect to the Eye of Satoshi's API. Server cannot be reached") + return None + + except requests.exceptions.Timeout: + logger.error("The request timed out") + return None + + def load_keys(teos_pk_path, cli_sk_path, cli_pk_path): """ Loads all the keys required so sign, send, and verify the appointment. @@ -426,6 +459,11 @@ def main(args, command_line_conf): if appointment_data: print(appointment_data) + elif command == "get_all_appointments": + appointment_data = get_all_appointments(teos_url) + if appointment_data: + print(appointment_data) + elif command == "help": if args: command = args.pop(0) @@ -442,6 +480,9 @@ def main(args, command_line_conf): else: logger.error("Unknown command. Use help to check the list of available commands") + elif command == "get_all_appointments": + sys.exit(help_get_all_appointments()) + else: sys.exit(show_usage()) @@ -457,7 +498,7 @@ def main(args, command_line_conf): if __name__ == "__main__": command_line_conf = {} - commands = ["register", "add_appointment", "get_appointment", "help"] + commands = ["register", "add_appointment", "get_appointment", "get_all_appointments", "help"] try: opts, args = getopt(argv[1:], "s:p:h", ["server", "port", "help"]) diff --git a/test/cli/unit/test_teos_cli.py b/test/cli/unit/test_teos_cli.py index 52a32d6..da6742a 100644 --- a/test/cli/unit/test_teos_cli.py +++ b/test/cli/unit/test_teos_cli.py @@ -4,7 +4,8 @@ import shutil import responses from binascii import hexlify from coincurve import PrivateKey -from requests.exceptions import ConnectionError +from requests.exceptions import ConnectionError, Timeout +from http.client import HTTPException import common.cryptographer from common.logger import Logger @@ -31,6 +32,7 @@ teos_url = "http://{}:{}".format(config.get("TEOS_SERVER"), config.get("TEOS_POR add_appointment_endpoint = "{}/add_appointment".format(teos_url) register_endpoint = "{}/register".format(teos_url) get_appointment_endpoint = "{}/get_appointment".format(teos_url) +get_all_appointments_endpoint = "{}/get_all_appointments".format(teos_url) dummy_appointment_data = { "tx": get_random_value_hex(192), @@ -194,7 +196,7 @@ def test_post_request(): @responses.activate def test_process_post_response(): - # Let's first crete a response + # Let's first create a response response = { "locator": dummy_appointment.to_dict()["locator"], "signature": get_signature(dummy_appointment.serialize(), dummy_teos_sk), @@ -257,3 +259,37 @@ def test_save_appointment_receipt(monkeypatch): assert any([dummy_appointment.locator in f for f in files]) shutil.rmtree(appointments_folder) + + +@responses.activate +def test_get_all_appointments(): + # Response of get_all_appointments endpoint is all appointments from watcher and responder. + dummy_appointment_dict["status"] = "being_watched" + response = {"watcher_appointments": dummy_appointment_dict, "responder_trackers": {}} + + request_url = get_all_appointments_endpoint + responses.add(responses.GET, request_url, json=response, status=200) + result = teos_cli.get_all_appointments(teos_url) + + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == request_url + assert json.loads(result).get("locator") == response.get("locator") + + +@responses.activate +def test_get_all_appointments_err(): + # Test that get_all_appointments handles a connection error appropriately. + request_url = get_all_appointments_endpoint + responses.add(responses.GET, request_url, body=ConnectionError()) + + assert not teos_cli.get_all_appointments(teos_url) + + # Test that get_all_appointments handles a timeout error appropriately. + responses.replace(responses.GET, request_url, body=Timeout()) + + assert not teos_cli.get_all_appointments(teos_url) + + # Test that get_all_appointments handles a 404 error appropriately. + responses.replace(responses.GET, request_url, status=404) + + assert teos_cli.get_all_appointments(teos_url) is None