From 1a2aac8c78832d2ffaf7ee08aabed42b32ad6b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20L=C3=A9onardi?= Date: Tue, 1 Oct 2024 17:34:56 +0200 Subject: [PATCH] Register a Validator module --- sekoia_automation/account_validator.py | 37 +++++++++++++ sekoia_automation/module.py | 4 ++ tests/test_account_validator.py | 77 ++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 sekoia_automation/account_validator.py create mode 100644 tests/test_account_validator.py diff --git a/sekoia_automation/account_validator.py b/sekoia_automation/account_validator.py new file mode 100644 index 0000000..b8d3439 --- /dev/null +++ b/sekoia_automation/account_validator.py @@ -0,0 +1,37 @@ +from abc import abstractmethod + +import requests + +from sekoia_automation.module import ModuleItem + + +class AccountValidator(ModuleItem): + VALIDATION_CALLBACK_URL_FILE_NAME = "validation_callback_url" + + @abstractmethod + def validator(self) -> bool: + """To define in subclasses. Validates the configuration of the module. + + Returns: + bool: True if the module is valid, False otherwise + """ + + def execute(self): + """Validates the account (module_configuration) of the module + and sends the result to Symphony.""" + # Call the actual validation procedure + status = self.validator() + + # Return result of validation to Symphony + data = {"validation_status": status} + validation_callback_url = self.module.load_config( + self.VALIDATION_CALLBACK_URL_FILE_NAME + ) + response = requests.request( + "POST", + validation_callback_url, + json=data, + headers={"Authorization": f"Bearer {self.token}"}, + timeout=30, + ) + response.raise_for_status() diff --git a/sekoia_automation/module.py b/sekoia_automation/module.py index b702b7f..ce63d17 100644 --- a/sekoia_automation/module.py +++ b/sekoia_automation/module.py @@ -13,6 +13,7 @@ from pydantic import BaseModel from requests import HTTPError, Response +from sekoia_automation.account_validator import AccountValidator from sekoia_automation.config import load_config from sekoia_automation.exceptions import ( CommandNotFoundError, @@ -269,6 +270,9 @@ def register(self, item: type["ModuleItem"], name: str = ""): item.name = name self._items[name] = item + def register_account_validator(self): + self.register(AccountValidator, "validate_module_configuration") + def run(self): command = self.command or "" diff --git a/tests/test_account_validator.py b/tests/test_account_validator.py new file mode 100644 index 0000000..b074cb2 --- /dev/null +++ b/tests/test_account_validator.py @@ -0,0 +1,77 @@ +from unittest.mock import patch + +import pytest +import requests +import requests_mock + +from sekoia_automation.account_validator import AccountValidator + + +class TestAccountValidator(AccountValidator): + def validator(self) -> bool: + return True + + +def test_execute_success(): + validator = TestAccountValidator() + + with ( + patch.object( + validator.module, "load_config", return_value="http://example.com/callback" + ) as mock_load_config, + requests_mock.Mocker() as mock_request, + ): + mock_request.post("http://example.com/callback", status_code=200) + + validator.execute() + + mock_load_config.assert_called_once_with( + validator.VALIDATION_CALLBACK_URL_FILE_NAME + ) + assert mock_request.called + assert mock_request.last_request.json() == {"validation_status": True} + + +def test_execute_failure(): + class FailingAccountValidator(AccountValidator): + def validator(self) -> bool: + return False + + validator = FailingAccountValidator() + + with ( + patch.object( + validator.module, "load_config", return_value="http://example.com/callback" + ) as mock_load_config, + requests_mock.Mocker() as mock_request, + ): + mock_request.post("http://example.com/callback", status_code=200) + + validator.execute() + + mock_load_config.assert_called_once_with( + validator.VALIDATION_CALLBACK_URL_FILE_NAME + ) + assert mock_request.called + assert mock_request.last_request.json() == {"validation_status": False} + + +def test_execute_request_failure(): + validator = TestAccountValidator() + + with ( + patch.object( + validator.module, "load_config", return_value="http://example.com/callback" + ) as mock_load_config, + requests_mock.Mocker() as mock_request, + ): + mock_request.post("http://example.com/callback", status_code=500) + + with pytest.raises(requests.exceptions.HTTPError): + validator.execute() + + mock_load_config.assert_called_once_with( + validator.VALIDATION_CALLBACK_URL_FILE_NAME + ) + assert mock_request.called + assert mock_request.last_request.json() == {"validation_status": True}