diff --git a/app/api/core_api.py b/app/api/core_api.py index 2b0c025..3def6d6 100644 --- a/app/api/core_api.py +++ b/app/api/core_api.py @@ -1,19 +1,30 @@ import json import os import configparser +import asyncio from flask import Blueprint, Flask, jsonify, render_template, send_file, abort, request from web3 import Web3 + from dark import DarkMap, DarkGateway from util.validation import ValidationUtil from util.config_manager import ConfigManager + # configurando classe das váriaveis externas config_manager = ConfigManager() core_api_blueprint = Blueprint("core_api", __name__, url_prefix="/core") +async_mode = config_manager.get_operation_mode() + +if async_mode == "ASYNC": + from util.asynchronus import add_url, add_external_pid, set_payload +else: + from util.synchronous import add_url, add_external_pid, set_payload + + ## # configuring dARK GW ## @@ -134,133 +145,6 @@ def get_pid_by_noid(nam, shoulder): return get_pid(dark_id) -# Set the external variable config_manager.set_url_validation("BASIC") -def add_url(ark_id, external_url): - try: - VERIFICATION_METHOD = config_manager.get_url_validation() - except: - VERIFICATION_METHOD = None - - try: - pid = None - if ark_id.startswith("0x"): - pid = dark_map.get_pid_by_hash(ark_id) - else: - pid = dark_map.get_pid_by_ark(ark_id) - - if VERIFICATION_METHOD == "BASIC": - if ValidationUtil.check_url(external_url) == False: - return jsonify({"error": "Invalid URL"}), 400 - elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: - if len(external_url) == 0: - return jsonify({"error": "Invalid URL"}), 400 - else: - return jsonify({"error": "the method could not be implemented"}), 400 - - dark_map.sync_set_url(pid.pid_hash, external_url) - - return ( - jsonify( - { - "pid": str(pid.ark), - "action": "external_url_add", - "parameter": external_url, - } - ), - 200, - ) - - except Exception as e: - return jsonify({"error": str(e)}), 400 - - -# Set the external variable config_manager.set_external_pid_validation("BASIC") -def add_external_pid(ark_id, external_pid): - try: - VERIFICATION_METHOD = config_manager.get_external_pid_validation() - - except: - VERIFICATION_METHOD = None - - try: - pid = None - - if ark_id.startswith("0x"): - pid = dark_map.get_pid_by_hash(ark_id) - else: - pid = dark_map.get_pid_by_ark(ark_id) - - if VERIFICATION_METHOD == "BASIC": - if external_pid.startswith("doi:/") == False: - return jsonify({"error": "Invalid Pid"}), 400 - - elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: - if len(external_pid) == 0: - return jsonify({"error": "Invalid Pid"}), 400 - else: - return jsonify({"error": "the method could not be implemented"}), 400 - - valid_pid = external_pid.split(":/")[1] - dark_map.sync_add_external_pid(pid.pid_hash, valid_pid) - - return ( - jsonify( - { - "pid": str(pid.ark), - "action": "external_pid_add", - "parameter": valid_pid, - } - ), - 200, - ) - - except Exception as e: - return jsonify({"error": str(e)}), 400 - - -# Set the external variable config_manager.set_payload_validation("BASIC") -def set_payload(ark_id, payload): - try: - VERIFICATION_METHOD = config_manager.get_payload_validation() - - except: - VERIFICATION_METHOD = None - - try: - if VERIFICATION_METHOD == "BASIC": - payload = json.loads(payload) - - if type(payload) != dict or len(payload) == 0: - return jsonify({"error": "Invalid JSON payload"}), 400 - - elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: - if type(payload) != dict or len(payload) == 0: - return jsonify({"error": "Invalid JSON payload"}), 400 - else: - return jsonify({"error": "the method could not be implemented"}), 400 - - if ark_id.startswith("0x"): - pid = dark_map.get_pid_by_hash(ark_id) - else: - pid = dark_map.get_pid_by_ark(ark_id) - - dark_map.sync_set_payload(pid.pid_hash, payload) - - return ( - jsonify( - { - "pid": str(pid.ark), - "action": "payload_add", - "parameter": payload, - } - ), - 200, - ) - - except Exception as e: - return jsonify({"error": str(e)}), 400 - - @core_api_blueprint.post("/set/") def set_general(ark_id): if request.is_json: @@ -280,14 +164,33 @@ def set_general(ark_id): if "external_url" in data: external_url = data.get("external_url") - return add_url(ark_id, external_url) + + if async_mode == "ASYNC": + return asyncio.run(add_url(ark_id, external_url)) + if async_mode == "SYNC": + return add_url(ark_id, external_url) if "external_pid" in data: pid = data.get("external_pid") - return add_external_pid(ark_id, pid) + + if async_mode == "ASYNC": + return asyncio.run(add_external_pid(ark_id, pid)) + if async_mode == "SYNC": + return add_external_pid(ark_id, pid) if "payload" in data: - payload = data - return set_payload(ark_id, payload) + payload = data.get("payload") - return jsonify({"error": "Invalid or missing data in the request. Please check your input and try again."}), 400 + if async_mode == "ASYNC": + return asyncio.run(set_payload(ark_id, payload)) + if async_mode == "SYNC": + return set_payload(ark_id, payload) + + return ( + jsonify( + { + "error": "Invalid or missing data in the request. Please check your input and try again." + } + ), + 400, + ) diff --git a/app/util/asynchronus.py b/app/util/asynchronus.py new file mode 100644 index 0000000..b33f05e --- /dev/null +++ b/app/util/asynchronus.py @@ -0,0 +1,162 @@ +import json +import os +import configparser + +from flask import ( + Blueprint, + Flask, + jsonify, + render_template, + send_file, + abort, + request, + make_response, +) +from web3 import Web3 + +from dark import DarkMap, DarkGateway +from util.validation import ValidationUtil +from util.responses import success_response, error_response +from util.config_manager import ConfigManager + + +# configurando classe das váriaveis externas +config_manager = ConfigManager() +async_mode = config_manager.get_operation_mode() + +## +# configuring dARK GW +## + +bc_config = configparser.ConfigParser() +deployed_contracts_config = configparser.ConfigParser() + +# bc configuration +PROJECT_ROOT = "./" +bc_config.read(os.path.join(PROJECT_ROOT, "config.ini")) +# deployed contracts config +deployed_contracts_config.read(os.path.join(PROJECT_ROOT, "deployed_contracts.ini")) + + +# gw +dark_gw = DarkGateway(bc_config, deployed_contracts_config) + +# +dark_map = DarkMap(dark_gw) + +### +### methods +### + + +# Set the external variable -> config_manager.set_url_validation("BASIC") +async def add_url(ark_id, external_url): + try: + VERIFICATION_METHOD = config_manager.get_url_validation() + except: + VERIFICATION_METHOD = None + + try: + action = "add_url" + pid = None + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if ValidationUtil.check_url(external_url) == False: + return make_response(error_response(pid,async_mode.lower(), action, external_url, "Invalid URL", 400)) + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if len(external_url) == 0: + return make_response(error_response(pid,async_mode.lower(), action, external_url, "Invalid URL", 400)) + else: + return make_response(error_response(pid,async_mode.lower(), action, external_url, "the method could not be implemented", 400)) + + tx_set = dark_map.async_set_url(pid.pid_hash, external_url) + + response = success_response(pid, async_mode.lower(), action, external_url, "queued", tx_receipt=tx_set.hex()) + + return response + + except Exception as e: + return make_response(jsonify({"error": str(e)}), 400) + + +# Set the external variable -> config_manager.set_external_pid_validation("BASIC") +async def add_external_pid(ark_id, external_pid): + try: + VERIFICATION_METHOD = config_manager.get_external_pid_validation() + + except: + VERIFICATION_METHOD = None + + try: + action = "add_external_pid" + pid = None + + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if external_pid.startswith("doi:/") == False: + return make_response(error_response(pid,async_mode.lower(), action, external_pid, "Invalid PID", 400)) + + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if len(external_pid) == 0: + return make_response(error_response(pid,async_mode.lower(), action, external_pid, "Invalid PID", 400)) + else: + return make_response(error_response(pid,async_mode.lower(), action, external_pid,"queued" ,"the method could not, be implemented", 400)) + + valid_pid = external_pid.split(":/")[1] + tx_set = dark_map.async_set_external_pid(pid.pid_hash, valid_pid) + + response = success_response(pid, async_mode.lower(), action, external_pid, "queued", tx_receipt=tx_set.hex()) + + return response + + except Exception as e: + return make_response(jsonify({"error": str(e)}), 400) + + +# Set the external variable -> config_manager.set_payload_validation("BASIC") +async def set_payload(ark_id, payload): + try: + VERIFICATION_METHOD = config_manager.get_payload_validation() + + except: + VERIFICATION_METHOD = None + + try: + action = "set_payload" + pid = None + + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if type(payload) != dict: + try: + payload = json.loads(payload) + except Exception as e: + return error_response(pid,async_mode.lower(), action, payload, "Invalid JSON payload", 400) + + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if len(payload) == 0: + return make_response(error_response(pid,async_mode.lower(), action, payload, "Invalid JSON payload", 400)) + else: + return make_response(error_response(pid,async_mode.lower(), action, payload, "the method could not be implemented", 400)) + + tx_set = dark_map.async_set_payload(pid.pid_hash, payload) + tx_status, tx_recipt = dark_gw.transaction_was_executed(tx_set) + + response = success_response(pid, async_mode.lower(), action, payload,"queued", tx_receipt=tx_set.hex()) + + return response + + except Exception as e: + return jsonify({"error": str(e)}), 400 diff --git a/app/util/config_manager.py b/app/util/config_manager.py index 50aca43..1972b37 100644 --- a/app/util/config_manager.py +++ b/app/util/config_manager.py @@ -6,10 +6,10 @@ class ConfigManager: EXTERNAL_URL_PARAMETER = "external_url" def __init__(self): - # Configuração padrão para variáveis de ambiente, se não definidas os.environ.setdefault("HYPERDRIVE_EXTERNAL_PID_VALIDATION", "NONE") os.environ.setdefault("HYPERDRIVE_URL_VALIDATION", "NONE") os.environ.setdefault("HYPERDRIVE_PAYLOAD_VALIDATION", "NONE") + os.environ.setdefault("HYPERDRIVE_OPERATION_MODE", "ASYNC") def get_external_pid_validation(self): return os.environ["HYPERDRIVE_EXTERNAL_PID_VALIDATION"] @@ -28,3 +28,9 @@ def get_payload_validation(self): def set_payload_validation(self, value): os.environ["HYPERDRIVE_PAYLOAD_VALIDATION"] = value + + def get_operation_mode(self): + return os.environ["HYPERDRIVE_OPERATION_MODE"] + + def set_operation_mode(self, value): + os.environ["HYPERDRIVE_OPERATION_MODE"] = value diff --git a/app/util/responses.py b/app/util/responses.py new file mode 100644 index 0000000..dabb22e --- /dev/null +++ b/app/util/responses.py @@ -0,0 +1,42 @@ +from flask import jsonify +from web3 import Web3 + + +def success_response(pid, op_mode, action, parameter,status, tx_receipt=None, tx_status=None): + if op_mode == "sync": + status = "executed" + else: + status = "queued" + + response_data = { + "pid": str(pid.ark), + "pid_hash_index": Web3.toHex(pid.pid_hash), + "action": action, + "parameter": {"pid": str(pid.ark), action: parameter}, + "status": status, + "hyperdrive_op_mode": op_mode.lower(), + } + + if tx_receipt != None: + response_data["transaction_hash"] = tx_receipt + + if tx_status != None: + response_data["tx_status"] = tx_status + + return jsonify(response_data), 200 + + +def error_response(pid, op_mode, action, parameter, error_message, error_code): + error_response = { + "error": { + "pid": str(pid.ark), + "pid_hash_index": Web3.toHex(pid.pid_hash), + "message": error_message, + "error_code": error_code, + "action": action, + "parameter": {"pid": str(pid.ark), "external_url": parameter}, + "status": "rejected", + "hyperdrive_op_mode": op_mode.lower(), + } + } + return jsonify(error_response), 400 diff --git a/app/util/synchronous.py b/app/util/synchronous.py new file mode 100644 index 0000000..57f09d5 --- /dev/null +++ b/app/util/synchronous.py @@ -0,0 +1,154 @@ +import json +import os +import configparser + +from flask import Blueprint, Flask, jsonify, render_template, send_file, abort, request +from web3 import Web3 + +from dark import DarkMap, DarkGateway +from util.validation import ValidationUtil +from util.responses import success_response, error_response +from util.config_manager import ConfigManager + +# configurando classe das váriaveis externas +config_manager = ConfigManager() +async_mode = config_manager.get_operation_mode() + +core_api_blueprint = Blueprint("core_api", __name__, url_prefix="/core") + +## +# configuring dARK GW +## + +bc_config = configparser.ConfigParser() +deployed_contracts_config = configparser.ConfigParser() + +# bc configuration +PROJECT_ROOT = "./" +bc_config.read(os.path.join(PROJECT_ROOT, "config.ini")) +# deployed contracts config +deployed_contracts_config.read(os.path.join(PROJECT_ROOT, "deployed_contracts.ini")) + + +# gw +dark_gw = DarkGateway(bc_config, deployed_contracts_config) + +# +dark_map = DarkMap(dark_gw) + +### +### methods +### + + +# Set the external variable -> config_manager.set_url_validation("BASIC") +def add_url(ark_id, external_url): + try: + VERIFICATION_METHOD = config_manager.get_url_validation() + except: + VERIFICATION_METHOD = None + + try: + action = "add_url" + pid = None + + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if ValidationUtil.check_url(external_url) == False: + return error_response(pid,async_mode.lower(), action, external_url,"Invalid URL", 400) + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if len(external_url) == 0: + return error_response(pid,async_mode.lower(), action, external_url,"Invalid URL", 400) + else: + return error_response(pid,async_mode.lower(), action, external_url, "the method could not be implemented", 400) + + dark_map.sync_set_url(pid.pid_hash, external_url) + + response = success_response(pid, async_mode.lower(), action, external_url, "executed") + + return response + + except Exception as e: + return jsonify({"error": str(e)}), 400 + + +# Set the external variable -> config_manager.set_external_pid_validation("BASIC") +def add_external_pid(ark_id, external_pid): + try: + VERIFICATION_METHOD = config_manager.get_external_pid_validation() + + except: + VERIFICATION_METHOD = None + + try: + action = "add_external_pid" + pid = None + + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if external_pid.startswith("doi:/") == False: + return error_response(pid,async_mode.lower(), action, external_pid, "Invalid PID", 400) + + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if len(external_pid) == 0: + return error_response(pid,async_mode.lower(), action, external_pid, "Invalid PID", 400) + else: + return error_response(pid,async_mode.lower(), action, external_pid,"queued" ,"the method could not, be implemented", 400) + + valid_pid = external_pid.split(":/")[1] + dark_map.sync_add_external_pid(pid.pid_hash, valid_pid) + + response = success_response(pid, async_mode.lower(), action, external_pid, "executed") + + return response + + except Exception as e: + return jsonify({"error": str(e)}), 400 + + +# Set the external variable -> config_manager.set_payload_validation("BASIC") +def set_payload(ark_id, payload): + try: + VERIFICATION_METHOD = config_manager.get_payload_validation() + + except: + VERIFICATION_METHOD = None + + try: + action = "set_payload" + pid = None + + if ark_id.startswith("0x"): + pid = dark_map.get_pid_by_hash(ark_id) + else: + pid = dark_map.get_pid_by_ark(ark_id) + + if VERIFICATION_METHOD == "BASIC": + if type(payload) != dict: + try: + payload = json.loads(payload) + except Exception as e: + return error_response(pid,async_mode.lower(), action, payload, "Invalid JSON payload", 400) + + elif VERIFICATION_METHOD == "NONE" or VERIFICATION_METHOD == None: + if type(payload) != dict or len(payload) == 0: + return error_response(pid,async_mode.lower(), action, payload, "Invalid JSON payload", 400) + else: + return error_response(pid,async_mode.lower(), action, payload, "the method could not be implemented", 400) + + dark_map.sync_set_payload(pid.pid_hash, payload) + + response = success_response(pid, async_mode.lower(), action, payload,"executed") + + return response + + except Exception as e: + return jsonify({"error": str(e)}), 400 diff --git a/tests/unit/tests_validation_methods.py b/tests/unit/tests_validation_methods.py new file mode 100644 index 0000000..de9e7e7 --- /dev/null +++ b/tests/unit/tests_validation_methods.py @@ -0,0 +1,89 @@ +import pytest +import json +from app.util.validation import ValidationUtil + +# external url validation tests + +def test_add_url_valid(): + + url = "http://www.hyperdrive.com/123456" + assert ValidationUtil.check_url(url) is True + +def test_add_url_invalid(): + + url = "hello 123" + assert ValidationUtil.check_url(url) is False + +def test_add_empty_url(): + + url = "" + assert ValidationUtil.check_url(url) is False + +def test_check_url_invalid_none(): + + url = None + assert ValidationUtil.check_url(url) is False + +# external pid validation tests + +def mock_validation_pid(external_pid): + + if external_pid.startswith("doi:/"): + valid_pid = external_pid.split(":/")[1] + return valid_pid + else: + return False + +def test_mock_validation_pid_valid_pid(): + + external_pid = "doi:/116.jdakt.7892" + result = mock_validation_pid(external_pid) + assert result == "116.jdakt.7892" + +def test_mock_validation_pid_invalid_pid(): + + external_pid = "DOIXPTO" + result = mock_validation_pid(external_pid) + assert result is False + +def test_mock_validation_pid_empty_pid(): + + external_pid = "" + result = mock_validation_pid(external_pid) + assert result is False + +# payload validation tests +def mock_validation_payload(payload): + + if type(payload) != dict: + try: + payload = json.loads(payload) + return payload + except Exception: + return False + else: + return payload + +def test_mock_validation_payload_valid_dict(): + + payload = {"key": "value", "number": 42} + result = mock_validation_payload(payload) + assert result == payload + +def test_mock_validation_payload_valid_json_str(): + + payload_str = '{"key": "value", "number": 42}' + result = mock_validation_payload(payload_str) + assert result == json.loads(payload_str) + +def test_mock_validation_payload_invalid_json_str(): + + payload_str = "{x : y}" + result = mock_validation_payload(payload_str) + assert result is False + +def test_mock_validation_payload_invalid_type(): + + payload = 123 + result = mock_validation_payload(payload) + assert result is False