From 60cdb5a3c56c1e662a835a5efdd4363543fc1ff9 Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 31 Mar 2024 15:40:48 +0800 Subject: [PATCH] feat: add jq source --- docs/usage.rst | 25 ++++++++++++++++++++++++ nvchecker_source/jq.py | 43 ++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 2 ++ tests/test_jq.py | 33 ++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 nvchecker_source/jq.py create mode 100644 tests/test_jq.py diff --git a/docs/usage.rst b/docs/usage.rst index 994044b..9afa157 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -326,6 +326,31 @@ post_data_type An additional dependency "lxml" is required. You can use ``pip install 'nvchecker[htmlparser]'``. +Search with an JSON Parser (jq) +~~~~~~~~~~~~~~~~~~~~~~~~~~ +:: + + source = "jq" + +Send an HTTP request and search through the body with a specific ``jq`` filter. + +url + The URL of the HTTP request. + +filter + An ``jq`` filter used to find the version string. + +post_data + (*Optional*) When present, a ``POST`` request (instead of a ``GET``) will be used. The value should be a string containing the full body of the request. The encoding of the string can be specified using the ``post_data_type`` option. + +post_data_type + (*Optional*) Specifies the ``Content-Type`` of the request body (``post_data``). By default, this is ``application/json``. + +This source supports :ref:`list options`. + +.. note:: + An additional dependency "jq.py" is required. + Find with a Command ~~~~~~~~~~~~~~~~~~~ :: diff --git a/nvchecker_source/jq.py b/nvchecker_source/jq.py new file mode 100644 index 0000000..72b4214 --- /dev/null +++ b/nvchecker_source/jq.py @@ -0,0 +1,43 @@ +# MIT licensed +# Copyright (c) 2024 Rocket Aaron , et al. + +import json +import jq + +from nvchecker.api import session, GetVersionError + +async def get_version(name, conf, *, cache, **kwargs): + key = tuple(sorted(conf.items())) + return await cache.get(key, get_version_impl) + +async def get_version_impl(info): + conf = dict(info) + + try: + program = jq.compile(conf.get('filter', '.')) + except ValueError as e: + raise GetVersionError('bad jq filter', exc_info=e) + + encoding = conf.get('encoding') + data = conf.get('post_data') + if data is None: + res = await session.get(conf['url']) + else: + res = await session.post(conf['url'], body = data, headers = { + 'Content-Type': conf.get('post_data_type', 'application/json') + }) + + try: + obj = json.loads(res.body) + except json.decoder.JSONDecodeError as e: + raise GetVersionError('bad json string', exc_info=e) + + try: + version = program.input(obj).all() + if version == [None] and not conf.get('missing_ok', False): + raise GetVersionError('version string not found.') + version = [str(v) for v in version] + except ValueError as e: + raise GetVersionError('failed to filter json', exc_info=e) + + return version diff --git a/setup.cfg b/setup.cfg index aac2757..b395bd5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,6 +63,8 @@ pypi = packaging htmlparser = lxml +jq = + jq [options.entry_points] console_scripts = diff --git a/tests/test_jq.py b/tests/test_jq.py new file mode 100644 index 0000000..16c7ba5 --- /dev/null +++ b/tests/test_jq.py @@ -0,0 +1,33 @@ +# MIT licensed +# Copyright (c) 2024 Rocket Aaron , et al. + +import pytest + +jq_available = True +try: + import jq +except jq: + jq_available = False + +pytestmark = [ + pytest.mark.asyncio(scope="session"), + pytest.mark.needs_net, + pytest.mark.skipif(not jq_available, reason="needs jq"), +] + +async def test_jq(get_version): + ver = await get_version("aur", { + "source": "jq", + "url": "https://aur.archlinux.org/rpc/v5/info?arg[]=nvchecker-git" + }) + ver = ver.strip() + assert ver.startswith("{") + assert ver.endswith("}") + +async def test_jq_filter(get_version): + ver = await get_version("aur", { + "source": "jq", + "url": "https://aur.archlinux.org/rpc/v5/info?arg[]=nvchecker-git", + "filter": '.results[0].PackageBase', + }) + assert ver == "nvchecker-git"