From 00a2137d5b3b9c6da6d353a572dfcf285c8f424b Mon Sep 17 00:00:00 2001 From: Sergi Delgado Segura Date: Tue, 9 Jul 2019 17:10:25 +0100 Subject: [PATCH] Improves pisa-cli Big refactor of the cli, includes proper help messages and get_appointment command. Also creates a separate folder for the cli (apps can be separated by folders). --- .gitignore | 2 +- pisa-btc/apps/__init__.py | 2 - pisa-btc/apps/cli/__init__.py | 2 + pisa-btc/apps/cli/help.py | 21 +++++++ pisa-btc/apps/cli/pisa-cli.py | 109 ++++++++++++++++++++++------------ 5 files changed, 95 insertions(+), 41 deletions(-) create mode 100644 pisa-btc/apps/cli/__init__.py create mode 100644 pisa-btc/apps/cli/help.py diff --git a/.gitignore b/.gitignore index f4f2fb8..d52868f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ bitcoin.conf* *__pycache__ .pending* pisa.log -pisa-btc/apps/*.json +pisa-btc/apps/cli/*.json .\#* build/ diff --git a/pisa-btc/apps/__init__.py b/pisa-btc/apps/__init__.py index 3281608..e69de29 100644 --- a/pisa-btc/apps/__init__.py +++ b/pisa-btc/apps/__init__.py @@ -1,2 +0,0 @@ -DEFAULT_PISA_API_SERVER = 'localhost' -DEFAULT_PISA_API_PORT = 9814 \ No newline at end of file diff --git a/pisa-btc/apps/cli/__init__.py b/pisa-btc/apps/cli/__init__.py new file mode 100644 index 0000000..3281608 --- /dev/null +++ b/pisa-btc/apps/cli/__init__.py @@ -0,0 +1,2 @@ +DEFAULT_PISA_API_SERVER = 'localhost' +DEFAULT_PISA_API_PORT = 9814 \ No newline at end of file diff --git a/pisa-btc/apps/cli/help.py b/pisa-btc/apps/cli/help.py new file mode 100644 index 0000000..d39020e --- /dev/null +++ b/pisa-btc/apps/cli/help.py @@ -0,0 +1,21 @@ +def help_add_appointment(): + return "NAME:" \ + "\tpython pisa-cli add_appointment - Registers a json formatted appointment to the PISA server." \ + "\n\nUSAGE:" \ + "\tpython pisa-cli add_appointment [command options] appointment/path_to_appointment_file" \ + "\n\nDESCRIPTION:" \ + "\n\n\tRegisters a json formatted appointment to the PISA server." \ + "\n\tif -f, --file *is* specified, the the command expects a path to a json file instead of a json encoded " \ + "\n\tstring as parameter." \ + "\n\nOPTIONS:" \ + "\n\t -f, --file path_to_json_file\t loads the appointment data from the specified json file instead of" \ + "\n\t\t\t\t\t command line" + + +def help_get_appointment(): + return "NAME:" \ + "\tpython pisa-cli get_appointment - Gets json formatted data about an appointment from the PISA server." \ + "\n\nUSAGE:" \ + "\tpython pisa-cli get_appointment appointment_locator" \ + "\n\nDESCRIPTION:" \ + "\n\n\tGets json formatted data about an appointment from the PISA server.\n" diff --git a/pisa-btc/apps/cli/pisa-cli.py b/pisa-btc/apps/cli/pisa-cli.py index 6fc4dfa..860eef9 100644 --- a/pisa-btc/apps/cli/pisa-cli.py +++ b/pisa-btc/apps/cli/pisa-cli.py @@ -1,20 +1,21 @@ -import requests import re import os import sys import json -from getopt import getopt, GetoptError -from sys import argv import logging +import requests +from sys import argv +from getopt import getopt, GetoptError from conf import CLIENT_LOG_FILE from hashlib import sha256 from binascii import unhexlify -from apps.blob import Blob +from apps.cli.blob import Blob from requests import ConnectTimeout, ConnectionError -from apps import DEFAULT_PISA_API_SERVER, DEFAULT_PISA_API_PORT +from apps.cli import DEFAULT_PISA_API_SERVER, DEFAULT_PISA_API_PORT +from apps.cli.help import help_add_appointment, help_get_appointment -def add_appointment(args): +def add_appointment(args, debug): use_help = "Use 'help add_appointment' for help of how to use the command." if args: @@ -45,7 +46,7 @@ def add_appointment(args): valid_locator = check_txid_format(appointment_data.get('tx_id')) if valid_locator: - pisa_url = "http://{}:{}".format(pisa_api_server, pisa_api_port) + add_appointment_endpoint = "http://{}:{}".format(pisa_api_server, pisa_api_port) appointment = build_appointment(appointment_data.get('tx'), appointment_data.get('tx_id'), appointment_data.get('start_time'), appointment_data.get('end_time'), appointment_data.get('dispute_delta'), debug, logging) @@ -54,7 +55,7 @@ def add_appointment(args): logging.info("[Client] sending appointment to PISA") try: - r = requests.post(url=pisa_url, json=json.dumps(appointment), timeout=5) + r = requests.post(url=add_appointment_endpoint, json=json.dumps(appointment), timeout=5) if debug: logging.info("[Client] {} (code: {})".format(r.text, r.status_code)) @@ -76,6 +77,43 @@ def add_appointment(args): raise sys.exit("The provided locator is not valid.") +def get_appointment(args): + + if args: + arg_opt = args.pop(0) + + if arg_opt in ['-h', '--help']: + sys.exit(help_get_appointment()) + else: + locator = arg_opt + valid_locator = check_txid_format(locator) + + if valid_locator: + get_appointment_endpoint = "http://{}:{}/get_appointment".format(pisa_api_server, pisa_api_port) + parameters = "?locator={}".format(locator) + try: + r = requests.get(url=get_appointment_endpoint+parameters, timeout=5) + + print(json.dumps(r.text)) + + except ConnectTimeout: + if debug: + logging.info("[Client] can't connect to pisa API. Connection timeout") + else: + sys.exit("Can't connect to pisa API. Connection timeout.") + + except ConnectionError: + if debug: + logging.info("[Client] can't connect to pisa API. Server cannot be reached") + else: + sys.exit("Can't connect to pisa API. Server cannot be reached.") + + else: + sys.exit("The provided locator is not valid.") + else: + raise sys.exit("The provided locator is not valid.") + + def build_appointment(tx, tx_id, start_block, end_block, dispute_delta, debug, logging): locator = sha256(unhexlify(tx_id)).hexdigest() @@ -97,37 +135,38 @@ def build_appointment(tx, tx_id, start_block, end_block, dispute_delta, debug, l def check_txid_format(txid): if len(txid) != 64: - raise Exception("txid does not matches the expected size (32-byte / 64 hex chars).") + raise sys.exit("locator does not matches the expected size (32-byte / 64 hex chars).") return re.search(r'^[0-9A-Fa-f]+$', txid) is not None -def show_usage(show_and_quit=False): - print("USAGE: " - "\n\tpython pisa-cli.py [global options] command [command options] [arguments]" - "\n\nCOMMANDS:" - "\n\tadd_appointment \tRegisters a json formatted appointment to the PISA server." - "\n\tget_appointment \tGets json formatted data about an appointment from the PISA server." - "\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 __init__.py)" - "\n\t-p, --port \tAPI port where to send the requests. Defaults to 9814 (modifiable in __init__.py)" - "\n\t-d, --debug \tshows debug information and stores it in pisa.log" - "\n\t-h --help \tshows this message.") +def show_usage(): + return('USAGE: ' + '\n\tpython pisa-cli.py [global options] command [command options] [arguments]' + '\n\nCOMMANDS:' + '\n\tadd_appointment \tRegisters a json formatted appointment to the PISA server.' + '\n\tget_appointment \tGets json formatted data about an appointment from the PISA server.' + '\n\thelp \t\t\tShows a list of commands or help for a specific command.' - if show_and_quit: - exit(-1) + '\n\nGLOBAL OPTIONS:' + '\n\t-s, --server \tAPI server where to send the requests. Defaults to localhost (modifiable in __init__.py)' + '\n\t-p, --port \tAPI port where to send the requests. Defaults to 9814 (modifiable in __init__.py)' + '\n\t-d, --debug \tshows debug information and stores it in pisa.log' + '\n\t-h --help \tshows this message.') if __name__ == '__main__': debug = False - command = None - appointment_data = None pisa_api_server = DEFAULT_PISA_API_SERVER pisa_api_port = DEFAULT_PISA_API_PORT commands = ['add_appointment', 'get_appointment', 'help'] + # Configure logging + logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO, handlers=[ + logging.FileHandler(CLIENT_LOG_FILE), + logging.StreamHandler() + ]) + try: opts, args = getopt(argv[1:], 's:p:dh', ['server', 'port', 'debug', 'help']) @@ -144,32 +183,32 @@ if __name__ == '__main__': debug = True if opt in ['-h', '--help']: - show_usage() + sys.exit(show_usage()) if args: command = args.pop(0) if command in commands: if command == 'add_appointment': - appointment_data = add_appointment(args) + add_appointment(args, debug) elif command == 'get_appointment': - pass + get_appointment(args) elif command == 'help': if args: command = args.pop(0) if command == 'add_appointment': - pass + sys.exit(help_add_appointment()) elif command == "get_appointment": - pass + sys.exit(help_get_appointment()) else: sys.exit("Unknown command. Use help to check the list of available commands.") else: - show_usage() + sys.exit(show_usage()) else: sys.exit("Unknown command. Use help to check the list of available commands.") @@ -179,10 +218,4 @@ if __name__ == '__main__': except json.JSONDecodeError as e: print('Non-JSON encoded appointment passed as parameter.') - # Configure logging - logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO, handlers=[ - logging.FileHandler(CLIENT_LOG_FILE), - logging.StreamHandler() - ]) -