diff --git a/Pipfile b/Pipfile index 94103c5..3e54528 100644 --- a/Pipfile +++ b/Pipfile @@ -15,7 +15,7 @@ gitpython = ">=3.1.24" jsonpickle = ">=2.0.0" [dev-packages] -black = "==21.12b0" +black = "==22.3.0" pytest = ">=6.2.5" pytest-cov =">=3.0.0" pytest-mock =">=3.6.1" diff --git a/README.md b/README.md index 7d05829..ddca6a0 100644 --- a/README.md +++ b/README.md @@ -152,3 +152,24 @@ support `GET` requests. curl --location --request GET 'http://0.0.0.0:5000/api/semantics/dsad/asd' \ --header 'x-api-key: 05a2212d-9985-48d2-b54f-0fbc5ba28766' ``` + +* **Info** + + Returns information about the `EthTx` + + * **URL** + ```shell + /api/info + ``` + * **Method** + `GET` + * **Authorization** + * Required: + header: `x-api-key=[string]` **OR** query parameter: `api_key=[string]` + * **URL Params** + * None + * **Example** + ```shell + curl --location --request GET 'http://api/info' \ + --header 'x-api-key: 05a2212d-9985-48d2-b54f-0fbc5ba28766' + ``` \ No newline at end of file diff --git a/ethtx_ce/app/api/__init__.py b/ethtx_ce/app/api/__init__.py index 9eec7e4..187efbb 100644 --- a/ethtx_ce/app/api/__init__.py +++ b/ethtx_ce/app/api/__init__.py @@ -19,6 +19,7 @@ from .. import factory from .decorators import auth_required +from ..helpers import read_ethtx_versions def create_app( @@ -27,7 +28,10 @@ def create_app( """Returns API application instance.""" app = factory.create_app(__name__, __path__, settings_override) + app.name = "ethtx_ce/api" + app.ethtx = engine # init ethtx engine + read_ethtx_versions(app) return app diff --git a/ethtx_ce/app/api/endpoints/__init__.py b/ethtx_ce/app/api/endpoints/__init__.py index 56fad8c..81eddcb 100644 --- a/ethtx_ce/app/api/endpoints/__init__.py +++ b/ethtx_ce/app/api/endpoints/__init__.py @@ -10,5 +10,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from .info import info_bp from .semantics import semantics_bp from .transactions import transactions_bp diff --git a/ethtx_ce/app/api/endpoints/info.py b/ethtx_ce/app/api/endpoints/info.py new file mode 100644 index 0000000..f0ef0e2 --- /dev/null +++ b/ethtx_ce/app/api/endpoints/info.py @@ -0,0 +1,27 @@ +from flask import Blueprint, current_app + +from .. import api_route +from ..decorators import response +from ...helpers import get_latest_ethtx_version + +info_bp = Blueprint("api_info", __name__) + + +@api_route(info_bp, "/info") +@response(200) +def read_info(): + """Get info.""" + ethtx_version = current_app.config["ethtx_version"] + latest_ethtx_version = get_latest_ethtx_version() + + ethtx_ce_version = current_app.config["ethtx_ce_version"] + + return { + "ethtx": { + "version": ethtx_version, + "is_latest": ethtx_version == latest_ethtx_version, + }, + "ethtx_ce": { + "version": ethtx_ce_version, + }, + } diff --git a/ethtx_ce/app/frontend/__init__.py b/ethtx_ce/app/frontend/__init__.py index ab6f772..e36855a 100644 --- a/ethtx_ce/app/frontend/__init__.py +++ b/ethtx_ce/app/frontend/__init__.py @@ -16,8 +16,9 @@ from ethtx import EthTx from flask import Blueprint, Flask -from .deps import read_ethtx_versions + from .. import factory +from ..helpers import read_ethtx_versions def create_app( @@ -31,6 +32,7 @@ def create_app( template_folder="frontend/templates", static_folder="frontend/static", ) + app.name = "ethtx_ce/frontend" app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True diff --git a/ethtx_ce/app/frontend/deps.py b/ethtx_ce/app/frontend/deps.py index 39cff1d..2ce9a6b 100644 --- a/ethtx_ce/app/frontend/deps.py +++ b/ethtx_ce/app/frontend/deps.py @@ -11,17 +11,14 @@ # limitations under the License. import json import logging -import os import re import time from secrets import compare_digest -from typing import Tuple, Optional +from typing import Optional -import pkg_resources import requests -from flask import Flask, request +from flask import request from flask_httpauth import HTTPBasicAuth -from git import Repo from ..config import Config @@ -54,7 +51,9 @@ def get_eth_price() -> Optional[float]: or eth_price_update is None or (current_time - eth_price_update) > 60 ): - response = requests.get("https://api.coinbase.com/v2/prices/ETH-USD/buy") + response = requests.get( + "https://api.coinbase.com/v2/prices/ETH-USD/buy", timeout=2 + ) if response.status_code == 200: eth_price = float(json.loads(response.content)["data"]["amount"]) eth_price_update = time.time() @@ -62,50 +61,6 @@ def get_eth_price() -> Optional[float]: return eth_price -def read_ethtx_versions(app: Flask) -> None: - """Read ethtx and ethtx_ce versions.""" - ethtx_version = pkg_resources.get_distribution("ethtx").version - - try: - remote_url, sha = _get_version_from_git() - except Exception: - remote_url, sha = _get_version_from_docker() - - ethtx_ce_version = f"{_clean_up_git_link(remote_url)}/tree/{sha}" - - log.info("EthTx version: %s. EthTx CE version: %s", ethtx_version, ethtx_ce_version) - - app.config["ethtx_version"] = ethtx_version - app.config["ethtx_ce_version"] = ethtx_ce_version - - -def _get_version_from_git() -> Tuple[str, str]: - """Get EthTx CE version from .git""" - repo = Repo(__file__, search_parent_directories=True) - - remote_url = repo.remote("origin").url - sha = repo.head.commit.hexsha - short_sha = repo.git.rev_parse(sha, short=True) - - return remote_url, short_sha - - -def _get_version_from_docker() -> Tuple[str, str]: - """Get EthTx CE version from env.""" - return os.getenv("GIT_URL", ""), os.getenv("GIT_SHA", "") - - -def _clean_up_git_link(git_link: str) -> str: - """Clean up git link, delete .git extension, make https url.""" - if "@" in git_link: - git_link.replace(":", "/").replace("git@", "https://") - - if git_link.endswith(".git"): - git_link = git_link[:-4] - - return git_link - - def extract_tx_hash_from_req() -> str: """Extract tx hash from request url.""" hash_match = re.findall(r"(0x)?([A-Fa-f0-9]{64})", request.url) diff --git a/ethtx_ce/app/helpers.py b/ethtx_ce/app/helpers.py index 4539d7e..adf27a9 100644 --- a/ethtx_ce/app/helpers.py +++ b/ethtx_ce/app/helpers.py @@ -12,10 +12,14 @@ import importlib import logging +import os import pkgutil -from typing import Any, List +from typing import Any, List, Tuple, Optional +import pkg_resources +import requests from flask import Blueprint, Flask +from git import Repo log = logging.getLogger(__name__) @@ -59,3 +63,65 @@ def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] + + +def read_ethtx_versions(app: Flask) -> None: + """Read ethtx and ethtx_ce versions.""" + ethtx_version = pkg_resources.get_distribution("ethtx").version + + try: + remote_url, sha = _get_version_from_git() + except Exception: + remote_url, sha = _get_version_from_docker() + + ethtx_ce_version = f"{_clean_up_git_link(remote_url)}/tree/{sha}" + + log.info( + "%s: EthTx version: %s. EthTx CE version: %s", + app.name, + ethtx_version, + ethtx_ce_version, + ) + + app.config["ethtx_version"] = ethtx_version + app.config["ethtx_ce_version"] = ethtx_ce_version + + +def get_latest_ethtx_version() -> str: + """Get latest EthTx version.""" + package = "EthTx" + response = requests.get(f"https://pypi.org/pypi/{package}/json") + + if response.status_code != 200: + log.warning("Failed to get latest EthTx version from PyPI") + return "" + + log.info("Latest EthTx version: %s", response.json()["info"]["version"]) + return response.json()["info"]["version"] + + +def _get_version_from_git() -> Tuple[str, str]: + """Get EthTx CE version from .git""" + repo = Repo(__file__, search_parent_directories=True) + + remote_url = repo.remote("origin").url + sha = repo.head.commit.hexsha + short_sha = repo.git.rev_parse(sha, short=True) + + return remote_url, short_sha + + +def _get_version_from_docker() -> Tuple[str, str]: + """Get EthTx CE version from env.""" + return os.getenv("GIT_URL", ""), os.getenv("GIT_SHA", "") + + +def _clean_up_git_link(git_link: str) -> str: + """Clean up git link, delete .git extension, make https url.""" + if "@" in git_link: + git_link.replace(":", "/").replace("git@", "https://") + + if git_link.endswith(".git"): + git_link = git_link[:-4] + + return git_link