diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ # Add here external Python modules dependencies, one per line. Module names # should match https://pypi.python.org/pypi names. For the full spec or # dependency lines, see https://pip.readthedocs.org/en/1.1/requirements.html +Flask redis diff --git a/swh/counters/api/server.py b/swh/counters/api/server.py --- a/swh/counters/api/server.py +++ b/swh/counters/api/server.py @@ -4,7 +4,9 @@ # See top-level LICENSE file for more information import logging import os -from typing import Any, Dict +from typing import Any, Dict, Optional + +from flask import abort from swh.core import config from swh.core.api import RPCServerApp @@ -13,7 +15,7 @@ logger = logging.getLogger(__name__) -app = None +app: Optional[RPCServerApp] = None def make_app(config: Dict[str, Any]) -> RPCServerApp: @@ -26,16 +28,21 @@ backend_factory=lambda: get_counters(**config["counters"]), ) - app.add_backend_class( - backend_class=HistoryInterface, - backend_factory=lambda: get_history(**config["history"]), - ) - handler = logging.StreamHandler() app.logger.addHandler(handler) app.config["counters"] = get_counters(**config["counters"]) + if "history" in config: + app.add_backend_class( + backend_class=HistoryInterface, + backend_factory=lambda: get_history(**config["history"]), + ) + app.config["history"] = get_history(**config["history"]) + app.add_url_rule( + "/counters_history/", "history", get_history_file_content + ) + app.add_url_rule("/", "index", index) app.add_url_rule("/metrics", "metrics", get_metrics) @@ -121,3 +128,11 @@ response.append("") return "\n".join(response) + + +def get_history_file_content(filename: str): + assert app is not None + try: + return app.config["history"].get_history(filename) + except FileNotFoundError: + abort(404) diff --git a/swh/counters/tests/test_server.py b/swh/counters/tests/test_server.py --- a/swh/counters/tests/test_server.py +++ b/swh/counters/tests/test_server.py @@ -3,6 +3,7 @@ # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information +import json import re from typing import Any, Dict @@ -32,6 +33,25 @@ return _environment_config_file(tmp_path, monkeypatch, swh_counters_server_config) +@pytest.fixture +def history_test_client(tmp_path, monkeypatch): + cfg = { + "counters": {"cls": "redis", "host": "redis:6379"}, + "history": { + "cls": "prometheus", + "prometheus_host": "prometheus", + "prometheus_port": "9090", + "live_data_start": "0", + "cache_base_directory": "/tmp", + }, + } + _environment_config_file(tmp_path, monkeypatch, cfg) + + app = make_app_from_configfile() + app.config["TESTING"] = True + return app.test_client() + + def write_config_file(tmpdir, config_dict: Dict, name: str = "config.yml") -> str: """Prepare configuration file in `$tmpdir/name` with content `content`. @@ -143,3 +163,31 @@ ) m = re.search(pattern, response) assert data[collection] == int(m.group(1)) + + +def test_server_counters_history(history_test_client, mocker): + """Test the counters history file download""" + + expected_result = {"content": [[1, 1], [2, 2]]} + mock = mocker.patch("swh.counters.history.History.get_history") + mock.return_value = expected_result + + r = history_test_client.get("/counters_history/test.json") + + assert 200 == r.status_code + + response = r.get_data().decode("utf-8") + response_json = json.loads(response) + + assert response_json == expected_result + + +def test_server_counters_history_file_not_found(history_test_client, mocker): + """ensure a 404 is returned when the file doesn't exists""" + + mock = mocker.patch("swh.counters.history.History.get_history") + mock.side_effect = FileNotFoundError + + r = history_test_client.get("/counters_history/fake.json") + + assert 404 == r.status_code