diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ba2b272 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +default_install_hook_types: [pre-push] +default_stages: [pre-push] +repos: + - repo: local + hooks: + - id: pytest-check + name: pytest-check + entry: make + language: system + pass_filenames: false + always_run: true + args: ["pre-commit"] + stages: [pre-push] diff --git a/README.md b/README.md index d796a6a..18acb40 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ Serverless Functions represent an innovative approach to implementing and executing code in cloud computing environments. This architecture enables developers to create and execute code fragments in a granular manner, without concerns about the underlying infrastructure. +## Pre-Commit Setup + +Before pushing your changes, it's recommended to set up pre-commit to run automated tests locally. Run the following command (needs to be done once): + +```bash +pre-commit install +``` + ## OpenFaaS: Simplifying Serverless Functions Management OpenFaaS, or Open Functions as a Service, is an open-source platform simplifying the entire life cycle of Serverless Functions. It provides a highly flexible and scalable framework for developing and executing functions in both cloud environments and local infrastructures. diff --git a/das-query-engine/action_dispatcher.py b/das-query-engine/action_dispatcher.py index ea49419..1bceea2 100644 --- a/das-query-engine/action_dispatcher.py +++ b/das-query-engine/action_dispatcher.py @@ -1,8 +1,9 @@ from typing import Union + +from action_mapper import ActionMapper from actions import ActionType -from exceptions import UnknownActionDispatcher, PayloadMalformed +from exceptions import PayloadMalformed, UnknownActionDispatcher from validators import validate -from action_mapper import ActionMapper class ActionDispatcher: @@ -17,9 +18,7 @@ def dispatch( action_map = self.action_mapper.get_action_dispatcher(action_type) if action_map is None: - raise UnknownActionDispatcher( - f"Exception at dispatch: action {action_type} unknown" - ) + raise UnknownActionDispatcher(f"Exception at dispatch: action {action_type} unknown") action = action_map["action"] validator = action_map.get("validator", None) diff --git a/das-query-engine/action_mapper.py b/das-query-engine/action_mapper.py index 1199898..00fe9d1 100644 --- a/das-query-engine/action_mapper.py +++ b/das-query-engine/action_mapper.py @@ -1,15 +1,15 @@ -from actions import Actions +from typing import Any, Dict + +from actions import Actions, ActionType +from exceptions import UnknownActionDispatcher from validators.actions import ( GetAtomValidator, - GetNodeValidator, - GetLinkValidator, + GetIncomingLinksValidator, GetLinksValidator, + GetLinkValidator, + GetNodeValidator, QueryValidator, - GetIncomingLinksValidator, ) -from actions import ActionType -from exceptions import UnknownActionDispatcher -from typing import Any, Dict class ActionMapper: @@ -62,7 +62,5 @@ def get_action_dispatcher(self, action_type: ActionType) -> Dict[str, Any]: action_map = self._build_dispatcher().get(action_type) if action_map is None: - raise UnknownActionDispatcher( - f"Exception at dispatch: action {action_type} unknown" - ) + raise UnknownActionDispatcher(f"Exception at dispatch: action {action_type} unknown") return action_map diff --git a/das-query-engine/actions.py b/das-query-engine/actions.py index 4f97a0f..d710303 100644 --- a/das-query-engine/actions.py +++ b/das-query-engine/actions.py @@ -1,10 +1,10 @@ import os from enum import Enum -from hyperon_das import DistributedAtomSpace -from hyperon_das.utils import QueryOutputFormat -from typing import List, Dict, Any, Tuple -from utils.decorators import remove_none_args, execution_time_tracker +from typing import Any, Dict, List, Tuple + from exceptions import UnreachableConnection +from hyperon_das import DistributedAtomSpace +from utils.decorators import execution_time_tracker, remove_none_args class ActionType(str, Enum): diff --git a/das-query-engine/handler.py b/das-query-engine/handler.py index 8b833bb..d1d24b1 100644 --- a/das-query-engine/handler.py +++ b/das-query-engine/handler.py @@ -1,13 +1,14 @@ -import json import base64 +import json import traceback + +from action_dispatcher import ActionDispatcher +from action_mapper import ActionMapper +from exceptions import PayloadMalformed, UnknownActionDispatcher, UnreachableConnection from hyperon_das.logger import logger from utils.dotenv import load_env from validators import validate from validators.event import EventValidator -from action_dispatcher import ActionDispatcher -from exceptions import UnknownActionDispatcher, UnreachableConnection, PayloadMalformed -from action_mapper import ActionMapper load_env() @@ -15,25 +16,16 @@ def _response( http_code_response: int, result: str, - context: any = None, headers: dict = {}, ): logger().info(f"Function status code response - {http_code_response}") - if context is None: - try: - return json.loads(result) - except: - return result - return { "statusCode": http_code_response, "body": json.dumps(result), "headers": { "Content-Type": "application/json", - "X-Handler-Method-Timestamp": headers.get( - "X-Handler-Method-Timestamp", None - ), + "X-Handler-Method-Timestamp": headers.get("X-Handler-Method-Timestamp", None), }, } @@ -87,7 +79,6 @@ def handle(event: any, context=None): return _response( http_code_response, result, - context=context, headers={ "X-Handler-Method-Timestamp": elapsed_time, }, diff --git a/das-query-engine/tests/integration/handle/base_test_action.py b/das-query-engine/tests/integration/handle/base_test_action.py index 630609b..b5ffe77 100644 --- a/das-query-engine/tests/integration/handle/base_test_action.py +++ b/das-query-engine/tests/integration/handle/base_test_action.py @@ -1,7 +1,8 @@ -import pytest -from abc import ABC, abstractmethod -from handler import handle, UnreachableConnection import json +from abc import ABC, abstractmethod + +import pytest +from handler import UnreachableConnection, handle class BaseTestHandlerAction(ABC): diff --git a/das-query-engine/tests/integration/handle/test_count_atoms_action.py b/das-query-engine/tests/integration/handle/test_count_atoms_action.py index 71fbc7d..5f19435 100644 --- a/das-query-engine/tests/integration/handle/test_count_atoms_action.py +++ b/das-query-engine/tests/integration/handle/test_count_atoms_action.py @@ -1,6 +1,6 @@ +import pytest from actions import ActionType from tests.integration.handle.base_test_action import BaseTestHandlerAction -import pytest class TestCountAtomsAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/integration/handle/test_get_atom_action.py b/das-query-engine/tests/integration/handle/test_get_atom_action.py index 41b0254..a4a05f2 100644 --- a/das-query-engine/tests/integration/handle/test_get_atom_action.py +++ b/das-query-engine/tests/integration/handle/test_get_atom_action.py @@ -1,7 +1,7 @@ +import pytest from actions import ActionType -from tests.integration.handle.base_test_action import BaseTestHandlerAction from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher -import pytest +from tests.integration.handle.base_test_action import BaseTestHandlerAction class TestGetAtomAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/integration/handle/test_get_link_action.py b/das-query-engine/tests/integration/handle/test_get_link_action.py index 4c7ab21..e4356df 100644 --- a/das-query-engine/tests/integration/handle/test_get_link_action.py +++ b/das-query-engine/tests/integration/handle/test_get_link_action.py @@ -1,7 +1,7 @@ +import pytest from actions import ActionType -from tests.integration.handle.base_test_action import BaseTestHandlerAction from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher -import pytest +from tests.integration.handle.base_test_action import BaseTestHandlerAction class TestGetLinkAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/integration/handle/test_get_links_action.py b/das-query-engine/tests/integration/handle/test_get_links_action.py index 0232a3c..f8b0fff 100644 --- a/das-query-engine/tests/integration/handle/test_get_links_action.py +++ b/das-query-engine/tests/integration/handle/test_get_links_action.py @@ -1,7 +1,6 @@ +import pytest from actions import ActionType from tests.integration.handle.base_test_action import BaseTestHandlerAction -from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher -import pytest class TestGetLinksAction(BaseTestHandlerAction): @@ -11,9 +10,6 @@ def action_type(self): @pytest.fixture def valid_event(self, action_type): - human_handle = ExpressionHasher.terminal_hash("Concept", "human") - monkey_handle = ExpressionHasher.terminal_hash("Concept", "monkey") - return { "body": { "action": action_type, diff --git a/das-query-engine/tests/integration/handle/test_get_node_action.py b/das-query-engine/tests/integration/handle/test_get_node_action.py index 8869ed3..a10c214 100644 --- a/das-query-engine/tests/integration/handle/test_get_node_action.py +++ b/das-query-engine/tests/integration/handle/test_get_node_action.py @@ -1,7 +1,7 @@ +import pytest from actions import ActionType -from tests.integration.handle.base_test_action import BaseTestHandlerAction from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher -import pytest +from tests.integration.handle.base_test_action import BaseTestHandlerAction class TestGetNodeAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/integration/handle/test_ping_action.py b/das-query-engine/tests/integration/handle/test_ping_action.py index 2b82866..efb33c1 100644 --- a/das-query-engine/tests/integration/handle/test_ping_action.py +++ b/das-query-engine/tests/integration/handle/test_ping_action.py @@ -1,6 +1,6 @@ +import pytest from actions import ActionType from tests.integration.handle.base_test_action import BaseTestHandlerAction -import pytest class TestPingAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/integration/handle/test_query_action.py b/das-query-engine/tests/integration/handle/test_query_action.py index 6068571..63041b6 100644 --- a/das-query-engine/tests/integration/handle/test_query_action.py +++ b/das-query-engine/tests/integration/handle/test_query_action.py @@ -1,6 +1,6 @@ +import pytest from actions import ActionType from tests.integration.handle.base_test_action import BaseTestHandlerAction -import pytest class TestQueryAction(BaseTestHandlerAction): diff --git a/das-query-engine/tests/unit/test_action_dispatcher.py b/das-query-engine/tests/unit/test_action_dispatcher.py index e8b6160..19af7db 100644 --- a/das-query-engine/tests/unit/test_action_dispatcher.py +++ b/das-query-engine/tests/unit/test_action_dispatcher.py @@ -1,10 +1,11 @@ -import pytest from unittest.mock import MagicMock + +import pytest from action_dispatcher import ( ActionDispatcher, ActionType, - UnknownActionDispatcher, PayloadMalformed, + UnknownActionDispatcher, ) from validators.actions import GetAtomValidator diff --git a/das-query-engine/tests/unit/test_action_mapper.py b/das-query-engine/tests/unit/test_action_mapper.py index 6ef4331..287f272 100644 --- a/das-query-engine/tests/unit/test_action_mapper.py +++ b/das-query-engine/tests/unit/test_action_mapper.py @@ -1,8 +1,9 @@ import re +from unittest.mock import Mock + import pytest -from actions import ActionType from action_mapper import ActionMapper -from unittest.mock import Mock +from actions import ActionType from exceptions import UnknownActionDispatcher @@ -93,10 +94,7 @@ def test_build_dispatcher_commit_changes_action(): dispatchers = action_mapper._build_dispatcher() - assert ( - dispatchers[ActionType.COMMIT_CHANGES]["action"] - == expected_actions.commit_changes - ) + assert dispatchers[ActionType.COMMIT_CHANGES]["action"] == expected_actions.commit_changes assert dispatchers[ActionType.COMMIT_CHANGES]["validator"] is None diff --git a/das-query-engine/tests/unit/test_decorators.py b/das-query-engine/tests/unit/test_decorators.py index a0ea21b..9e870f8 100644 --- a/das-query-engine/tests/unit/test_decorators.py +++ b/das-query-engine/tests/unit/test_decorators.py @@ -1,6 +1,7 @@ # test_execution_helpers.py import time -from utils.decorators import remove_none_args, execution_time_tracker + +from utils.decorators import execution_time_tracker, remove_none_args def test_remove_none_args(): @@ -11,9 +12,7 @@ def example_function(arg1, arg2, kwarg1="kwarg1", kwarg2="kwarg2"): result = example_function(1, 2, kwarg1="value", kwarg2=None) assert result == (1, 2, "value", "kwarg2") - result_all_not_none = example_function( - 1, "2", kwarg1="value", kwarg2="another_value" - ) + result_all_not_none = example_function(1, "2", kwarg1="value", kwarg2="another_value") assert result_all_not_none == (1, "2", "value", "another_value") diff --git a/das-query-engine/tests/unit/test_handler.py b/das-query-engine/tests/unit/test_handler.py index e65e7ef..55c55f3 100644 --- a/das-query-engine/tests/unit/test_handler.py +++ b/das-query-engine/tests/unit/test_handler.py @@ -5,4 +5,4 @@ def test_handle_invalid_action(): payload = {"action": "INVALID_ACTION", "input": {}} result = handle(payload) - assert "error" in result + assert "body" in result diff --git a/das-query-engine/tests/unit/test_validators.py b/das-query-engine/tests/unit/test_validators.py index 4107908..3668252 100644 --- a/das-query-engine/tests/unit/test_validators.py +++ b/das-query-engine/tests/unit/test_validators.py @@ -2,24 +2,24 @@ def test_validate_dict_empty(): - assert validate_dict({}) == True + assert validate_dict({}) is True def test_validate_dict_non_empty(): - assert validate_dict({"key": "value"}) == True + assert validate_dict({"key": "value"}) is True def test_validate_dict_invalid_input(): - assert validate_dict("not a dict") == False + assert validate_dict("not a dict") is False def test_validate_dict_none_input(): - assert validate_dict(None) == False + assert validate_dict(None) is False def test_validate_dict_with_args(): - assert validate_dict({}, 1, 2, 3) == True + assert validate_dict({}, 1, 2, 3) is True def test_validate_dict_with_kwargs(): - assert validate_dict({}, key="value") == True + assert validate_dict({}, key="value") is True diff --git a/das-query-engine/utils/decorators.py b/das-query-engine/utils/decorators.py index 0d44579..2dc5265 100644 --- a/das-query-engine/utils/decorators.py +++ b/das-query-engine/utils/decorators.py @@ -1,5 +1,6 @@ import time from functools import wraps + from hyperon_das.logger import logger @@ -20,9 +21,7 @@ def wrapper(*args, **kwargs): result = func(*args, **kwargs) end_time = time.time() elapsed_time = end_time - start_time - logger().info( - f"The function '{func.__name__}' took {elapsed_time} seconds to execute" - ) + logger().info(f"The function '{func.__name__}' took {elapsed_time} seconds to execute") return result, elapsed_time return wrapper diff --git a/das-query-engine/utils/dotenv.py b/das-query-engine/utils/dotenv.py index 8b60e20..b636573 100644 --- a/das-query-engine/utils/dotenv.py +++ b/das-query-engine/utils/dotenv.py @@ -1,4 +1,5 @@ import os + from config import env_schema from hyperon_das.logger import logger @@ -15,11 +16,7 @@ def load_env(): raise Exception(error_message) var_value_display = ( - "*****" - if constraints["hidden"] - else var_value - if var_value != "" - else "(empty)" + "*****" if constraints["hidden"] else var_value if var_value != "" else "(empty)" ) env_log_entries.append(f"{var} - {var_value_display}") diff --git a/das-query-engine/validators/__init__.py b/das-query-engine/validators/__init__.py index e9fa190..57fec0a 100644 --- a/das-query-engine/validators/__init__.py +++ b/das-query-engine/validators/__init__.py @@ -1,6 +1,7 @@ import json -from incoming import PayloadValidator + from exceptions import PayloadMalformed +from incoming import PayloadValidator def validate(validator: PayloadValidator, payload: any) -> any: @@ -14,6 +15,6 @@ def validate(validator: PayloadValidator, payload: any) -> any: return payload except Exception as e: raise PayloadMalformed( - message=f"Exception at validate: payload malformed", + message="Exception at validate: payload malformed", details=str(e), ) diff --git a/das-query-engine/validators/actions.py b/das-query-engine/validators/actions.py index 784f303..21b04d8 100644 --- a/das-query-engine/validators/actions.py +++ b/das-query-engine/validators/actions.py @@ -1,4 +1,4 @@ -from incoming import datatypes, PayloadValidator +from incoming import PayloadValidator, datatypes class GetAtomValidator(PayloadValidator): @@ -28,6 +28,7 @@ class GetLinksValidator(PayloadValidator): target_types = datatypes.String(required=False) link_targets = datatypes.Array(required=False) + class GetIncomingLinksValidator(PayloadValidator): strict = True @@ -44,8 +45,6 @@ class QueryValidator(PayloadValidator): ) parameters = datatypes.Function( - lambda value, *args, **kwargs: isinstance(value, dict) - if value is not None - else True, + lambda value, *args, **kwargs: isinstance(value, dict) if value is not None else True, required=False, ) diff --git a/das-query-engine/validators/event.py b/das-query-engine/validators/event.py index 20fac43..77d12da 100644 --- a/das-query-engine/validators/event.py +++ b/das-query-engine/validators/event.py @@ -1,4 +1,4 @@ -from incoming import datatypes, PayloadValidator +from incoming import PayloadValidator, datatypes from validators.custom_validator_types import validate_dict diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9f247ad --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pre-commit~=3.6.0