From ec3340bb8933cb5478deb21045efc58053180f23 Mon Sep 17 00:00:00 2001 From: Fatih Acar Date: Thu, 19 Sep 2024 09:41:13 +0200 Subject: [PATCH 1/8] fix: allow id filters to be combined This can be useful if we want to do filtering directly when querying Infrahub. Signed-off-by: Fatih Acar --- infrahub_sdk/client.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index 8833f04b..3422535c 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -347,14 +347,14 @@ async def get( filters[schema.default_filter] = id else: filters["ids"] = [id] - elif hfid: + if hfid: if isinstance(schema, NodeSchema) and schema.human_friendly_id: filters["hfid"] = hfid else: raise ValueError("Cannot filter by HFID if the node doesn't have an HFID defined") - elif kwargs: - filters = kwargs - else: + if kwargs: + filters.update(kwargs) + if len(filters) == 0: raise ValueError("At least one filter must be provided to get()") results = await self.filters( @@ -1355,14 +1355,14 @@ def get( filters[schema.default_filter] = id else: filters["ids"] = [id] - elif hfid: + if hfid: if isinstance(schema, NodeSchema) and schema.human_friendly_id: filters["hfid"] = hfid else: raise ValueError("Cannot filter by HFID if the node doesn't have an HFID defined") - elif kwargs: - filters = kwargs - else: + if kwargs: + filters.update(kwargs) + if len(filters) == 0: raise ValueError("At least one filter must be provided to get()") results = self.filters( From 7551e4936c1e16816ea029cb20d1a43844c5306f Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Fri, 20 Sep 2024 06:35:35 +0200 Subject: [PATCH 2/8] Add back https://github.com/opsmill/infrahub/pull/4339 --- infrahub_sdk/node.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/infrahub_sdk/node.py b/infrahub_sdk/node.py index 66b9bb0f..b75ee77e 100644 --- a/infrahub_sdk/node.py +++ b/infrahub_sdk/node.py @@ -725,12 +725,11 @@ def get_human_friendly_id(self) -> Optional[list[str]]: if not self._schema.human_friendly_id: return None - # If all components of an HFID are null, we cannot identify a single node + # If an HFID component is missing we assume that it is invalid and not usable for this node hfid_components = [self.get_path_value(path=item) for item in self._schema.human_friendly_id] - if all(c is None for c in hfid_components): + if None in hfid_components: return None - - return [str(c) for c in hfid_components] + return [str(hfid) for hfid in hfid_components] def get_human_friendly_id_as_string(self, include_kind: bool = False) -> Optional[str]: hfid = self.get_human_friendly_id() From acbbfb22f2278ae239db4b652063017bae3ef1a1 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Fri, 20 Sep 2024 06:36:00 +0200 Subject: [PATCH 3/8] Add back https://github.com/opsmill/infrahub/pull/4367 --- infrahub_sdk/pytest_plugin/plugin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/infrahub_sdk/pytest_plugin/plugin.py b/infrahub_sdk/pytest_plugin/plugin.py index faddc78e..08d355c1 100644 --- a/infrahub_sdk/pytest_plugin/plugin.py +++ b/infrahub_sdk/pytest_plugin/plugin.py @@ -1,3 +1,4 @@ +import os from pathlib import Path from typing import Optional, Union @@ -34,6 +35,7 @@ def pytest_addoption(parser: Parser) -> None: action="store", dest="infrahub_key", metavar="INFRAHUB_TESTS_API_KEY", + default=os.getenv("INFRAHUB_API_TOKEN"), help="Key to use when querying the Infrahub instance for live testing", ) group.addoption( @@ -74,12 +76,11 @@ def pytest_sessionstart(session: Session) -> None: "default_branch": session.config.option.infrahub_branch, } if hasattr(session.config.option, "infrahub_key"): - client_config = {"api_token": session.config.option.infrahub_key} + client_config["api_token"] = session.config.option.infrahub_key elif hasattr(session.config.option, "infrahub_username") and hasattr(session.config.option, "infrahub_password"): - client_config = { - "username": session.config.option.infrahub_username, - "password": session.config.option.infrahub_password, - } + client_config.pop("api_token") + client_config["username"] = session.config.option.infrahub_username + client_config["password"] = session.config.option.infrahub_password infrahub_client = InfrahubClientSync(config=client_config) session.infrahub_client = infrahub_client # type: ignore[attr-defined] From c71cf053bf3273017be8d94640c10ae436ab3969 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Sun, 22 Sep 2024 10:44:01 +0200 Subject: [PATCH 4/8] Remove the message about the migration --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 89d1fec3..a9a657f4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -> ⚠️ **Warning** -> This repository is still a WORK IN PROGRESS. Soon it will be the new home of the Python SDK for Infrahub but for now the project still lives in the main repository -
- ![Infrahub Logo](https://assets-global.website-files.com/657aff4a26dd8afbab24944b/657b0e0678f7fd35ce130776_Logo%20INFRAHUB.svg) From 3393e394fe5d7d57acd1fa3ab28e8e39cf189ca8 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Tue, 24 Sep 2024 18:29:05 +0200 Subject: [PATCH 5/8] Update GHA to publish release to pypi --- .github/workflows/publish-python-sdk.yml | 8 ++------ .gitignore | 9 ++------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/publish-python-sdk.yml b/.github/workflows/publish-python-sdk.yml index 0cef5537..aa561c68 100644 --- a/.github/workflows/publish-python-sdk.yml +++ b/.github/workflows/publish-python-sdk.yml @@ -4,7 +4,7 @@ name: Publish Infrahub Python SDK on: # yamllint disable rule:truthy push: tags: - - "python-sdk-v*" + - "v*" jobs: publish_to_pypi: @@ -14,7 +14,7 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@v5" with: - python-version: "3.11" + python-version: "3.12" - name: "Install Poetry" uses: "snok/install-poetry@v1" @@ -35,7 +35,6 @@ jobs: - name: "Install Dependencies" run: "poetry install" - working-directory: "./python_sdk" if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - name: "Add PyPI secret" @@ -43,12 +42,9 @@ jobs: - name: "Poetry build" run: "poetry build" - working-directory: "./python_sdk" - name: "show output" run: "ls -la dist/" - working-directory: "./python_sdk" - name: "Poetry push PyPI" run: "poetry publish" - working-directory: "./python_sdk" diff --git a/.gitignore b/.gitignore index 7f2630f6..6d21724f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,6 @@ coverage.xml script.py **/*.local.* .vscode/settings.json -node_modules/* -development/docker-compose.override.yml -development/docker-compose.dev-override.yml .DS_Store .python-version .ruff_cache @@ -23,13 +20,11 @@ docs/build storage/* .coverage.* -python_sdk/dist/* +dist/* .benchmarks/* # Test reports **/*.csv # Generated files -generated/ -query_performance_results/ -sync/dist/ \ No newline at end of file +generated/ \ No newline at end of file From de1c173c0dc3701881c94cb2796778d4259593b2 Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Tue, 24 Sep 2024 21:08:46 +0200 Subject: [PATCH 6/8] Add changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd73b912..6162718b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the upcoming release can be found in . + +## [0.13.1.dev0](https://github.com/opsmill/infrahub-sdk-python/tree/v0.13.1.dev0) - 2024-09-24 + +### Added + +- Allow id filters to be combined when executing a query ([#3](https://github.com/opsmill/infrahub-sdk-python/issues/3)) + +### Fixed + +- Add ability to construct HFIDs from payload for upsert mutations ([#45](https://github.com/opsmill/infrahub-sdk-python/issues/45)) +- Fix pytest plugin integration tests unable to run because we were not properly setting the api_token configuration setting for the SDK. From 1f77af24716a6431d28689300346e434e6ac476b Mon Sep 17 00:00:00 2001 From: Damien Garros Date: Tue, 24 Sep 2024 21:09:00 +0200 Subject: [PATCH 7/8] Add towncrier to poetry --- poetry.lock | 93 +++++++++++++++++++++++++++++++++++++++++++++++--- pyproject.toml | 5 +-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9933022c..fdc0609a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -190,7 +190,7 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, @@ -521,6 +521,51 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -617,7 +662,7 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, @@ -658,7 +703,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, @@ -1721,6 +1766,27 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] +[[package]] +name = "towncrier" +version = "24.8.0" +description = "Building newsfiles for your project." +optional = false +python-versions = ">=3.8" +files = [ + {file = "towncrier-24.8.0-py3-none-any.whl", hash = "sha256:9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d"}, + {file = "towncrier-24.8.0.tar.gz", hash = "sha256:013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3"}, +] + +[package.dependencies] +click = "*" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +importlib-resources = {version = ">=5", markers = "python_version < \"3.10\""} +jinja2 = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["furo (>=2024.05.06)", "nox", "packaging", "sphinx (>=5)", "twisted"] + [[package]] name = "traitlets" version = "5.14.3" @@ -1972,6 +2038,25 @@ pyyaml = "*" [package.extras] dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [extras] all = ["Jinja2", "numpy", "numpy", "pyarrow", "pytest", "pyyaml", "rich", "toml", "typer"] ctl = ["Jinja2", "numpy", "numpy", "pyarrow", "pyyaml", "rich", "toml", "typer"] @@ -1980,4 +2065,4 @@ tests = ["Jinja2", "pytest", "pyyaml", "rich"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "dbf81a9b4c48319d7bb7da0b75ee59e103511e6c9add572a195ba0b5d3056796" +content-hash = "da1358d88082ae278014e41b9a65c96aa9db5d3c7cbfdcf734da48679f87d9e3" diff --git a/pyproject.toml b/pyproject.toml index a1929ec8..7401bc73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "infrahub-sdk" -version = "0.13.1-dev0" +version = "0.13.1" description = "Python Client to interact with Infrahub" authors = ["OpsMill "] readme = "README.md" @@ -64,6 +64,7 @@ ruff = "0.5.0" pytest-xdist = "^3.3.1" types-python-slugify = "^8.0.0.3" invoke = "^2.2.0" +towncrier = "^24.8.0" [tool.poetry.extras] ctl = ["Jinja2", "numpy", "pyarrow", "pyyaml", "rich", "toml", "typer"] @@ -346,7 +347,7 @@ max-complexity = 17 [tool.towncrier] -package = "infrahub-sdk" +package = "infrahub_sdk" directory = "changelog" filename = "CHANGELOG.md" start_string = "\n" From 35f510d95faf02abdbfd6536464883093eaeca14 Mon Sep 17 00:00:00 2001 From: wvandeun Date: Mon, 30 Sep 2024 00:14:37 +0200 Subject: [PATCH 8/8] fixes execute_graphql method that was not considering default_branch --- changelog/46.fixed.md | 1 + infrahub_sdk/client.py | 2 ++ tests/unit/sdk/conftest.py | 8 ++++---- tests/unit/sdk/test_client.py | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 changelog/46.fixed.md diff --git a/changelog/46.fixed.md b/changelog/46.fixed.md new file mode 100644 index 00000000..b21af46d --- /dev/null +++ b/changelog/46.fixed.md @@ -0,0 +1 @@ +`execute_graphql` method for InfrahubClient(Sync) now properly considers the `default_branch` setting diff --git a/infrahub_sdk/client.py b/infrahub_sdk/client.py index 3422535c..74f32d03 100644 --- a/infrahub_sdk/client.py +++ b/infrahub_sdk/client.py @@ -575,6 +575,7 @@ async def execute_graphql( _type_: _description_ """ + branch_name = branch_name or self.default_branch url = self._graphql_url(branch_name=branch_name, at=at) payload: dict[str, Union[str, dict]] = {"query": query} @@ -1112,6 +1113,7 @@ def execute_graphql( dict: The result of the GraphQL query or mutation. """ + branch_name = branch_name or self.default_branch url = self._graphql_url(branch_name=branch_name, at=at) payload: dict[str, Union[str, dict]] = {"query": query} diff --git a/tests/unit/sdk/conftest.py b/tests/unit/sdk/conftest.py index a870cfdb..680bc4ae 100644 --- a/tests/unit/sdk/conftest.py +++ b/tests/unit/sdk/conftest.py @@ -1958,25 +1958,25 @@ async def mock_rest_api_artifact_generate(httpx_mock: HTTPXMock) -> HTTPXMock: @pytest.fixture async def mock_query_mutation_schema_dropdown_add(httpx_mock: HTTPXMock) -> None: response = {"data": {"SchemaDropdownAdd": {"ok": True}}} - httpx_mock.add_response(method="POST", url="http://mock/graphql", json=response) + httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json=response) @pytest.fixture async def mock_query_mutation_schema_dropdown_remove(httpx_mock: HTTPXMock) -> None: response = {"data": {"SchemaDropdownRemove": {"ok": True}}} - httpx_mock.add_response(method="POST", url="http://mock/graphql", json=response) + httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json=response) @pytest.fixture async def mock_query_mutation_schema_enum_add(httpx_mock: HTTPXMock) -> None: response = {"data": {"SchemaEnumAdd": {"ok": True}}} - httpx_mock.add_response(method="POST", url="http://mock/graphql", json=response) + httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json=response) @pytest.fixture async def mock_query_mutation_schema_enum_remove(httpx_mock: HTTPXMock) -> None: response = {"data": {"SchemaEnumRemove": {"ok": True}}} - httpx_mock.add_response(method="POST", url="http://mock/graphql", json=response) + httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json=response) @pytest.fixture diff --git a/tests/unit/sdk/test_client.py b/tests/unit/sdk/test_client.py index 50dc0b34..eb0f0930 100644 --- a/tests/unit/sdk/test_client.py +++ b/tests/unit/sdk/test_client.py @@ -607,7 +607,7 @@ async def test_allocate_next_ip_prefix( assert ip_prefix.description.value == "test" -EXPECTED_ECHO = """URL: http://mock/graphql +EXPECTED_ECHO = """URL: http://mock/graphql/main QUERY: query GetTags($name: String!) {