diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index d543d9d4..0a2130a2 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -10,6 +10,7 @@ import re import json +from uuid import UUID from itertools import chain from ansible.module_utils.common.text.converters import to_text @@ -617,27 +618,37 @@ def _remove_arg_spec_default(self, data): return new_dict + def is_valid_uuid(self, match): + """Determine if the match is already UUID.""" + try: + uuid_obj = UUID(match) + except ValueError: + return False + return str(uuid_obj) == match + def _get_query_param_id(self, match, data): """Used to find IDs of necessary searches when required under _build_query_params :returns id (int) or data (dict): Either returns the ID or original data passed in :params match (str): The key within the user defined data that is required to have an ID :params data (dict): User defined data passed into the module """ - if isinstance(data.get(match), int): - return data[match] - else: - endpoint = CONVERT_TO_ID[match] - app = self._find_app(endpoint) - nb_app = getattr(self.nb, app) - nb_endpoint = getattr(nb_app, endpoint) - query_params = {QUERY_TYPES.get(match): data[match]} - result = self._nb_endpoint_get(nb_endpoint, query_params, match) + match_value = data.get(match) + if isinstance(match_value, int) or self.is_valid_uuid(match_value): + return match_value - if result: - return result.id - else: - return data + endpoint = CONVERT_TO_ID[match] + app = self._find_app(endpoint) + nb_app = getattr(self.nb, app) + nb_endpoint = getattr(nb_app, endpoint) + + query_params = {QUERY_TYPES.get(match): data[match]} + result = self._nb_endpoint_get(nb_endpoint, query_params, match) + + if result: + return result.id + else: + return data def _build_query_params( self, parent, module_data, user_query_params=None, child=None diff --git a/poetry.lock b/poetry.lock index d5042574..2982fb07 100644 --- a/poetry.lock +++ b/poetry.lock @@ -322,6 +322,34 @@ apipkg = ">=1.4" [package.extras] testing = ["pre-commit"] +[[package]] +name = "hypothesis" +version = "6.8.0" +description = "A library for property-based testing" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +attrs = ">=19.2.0" +sortedcontainers = ">=2.1.0,<3.0.0" + +[package.extras] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] +cli = ["click (>=7.0)", "black (>=19.10b0)"] +codemods = ["libcst (>=0.3.16)"] +dateutil = ["python-dateutil (>=1.4)"] +django = ["pytz (>=2014.1)", "django (>=2.2)"] +dpcontracts = ["dpcontracts (>=0.4)"] +ghostwriter = ["black (>=19.10b0)"] +lark = ["lark-parser (>=0.6.5)"] +numpy = ["numpy (>=1.9.0)"] +pandas = ["pandas (>=0.25)"] +pytest = ["pytest (>=4.6)"] +pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["importlib-resources (>=3.3.0)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] + [[package]] name = "idna" version = "2.10" @@ -739,6 +767,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "sortedcontainers" +version = "2.3.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "sphinx" version = "3.5.1" @@ -939,7 +975,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "558dc969e1687257f0052c846a224000f1a19ebd6f4d7a82a7a3c87921536d4c" +content-hash = "fcf2713f6b7a833e0b3829ee32ecdc1192013fda806e71f5f8281119f1084fc4" [metadata.files] aiocontextvars = [ @@ -1158,6 +1194,10 @@ execnet = [ {file = "execnet-1.8.0-py2.py3-none-any.whl", hash = "sha256:7a13113028b1e1cc4c6492b28098b3c6576c9dccc7973bfe47b342afadafb2ac"}, {file = "execnet-1.8.0.tar.gz", hash = "sha256:b73c5565e517f24b62dea8a5ceac178c661c4309d3aa0c3e420856c072c411b4"}, ] +hypothesis = [ + {file = "hypothesis-6.8.0-py3-none-any.whl", hash = "sha256:9915e1ae1a6701ef98c671687d83e54d310b5ca3e832ba2833b14b9bbfb36ef5"}, + {file = "hypothesis-6.8.0.tar.gz", hash = "sha256:23c384d36dfc19b0e44d94698d707a66e679f7dc2fda4b242e88feebafa95d1f"}, +] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -1490,6 +1530,10 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] +sortedcontainers = [ + {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, + {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"}, +] sphinx = [ {file = "Sphinx-3.5.1-py3-none-any.whl", hash = "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c"}, {file = "Sphinx-3.5.1.tar.gz", hash = "sha256:11d521e787d9372c289472513d807277caafb1684b33eb4f08f7574c405893a9"}, diff --git a/pyproject.toml b/pyproject.toml index 2e05b157..2862ac50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ antsibull = "^0.25.0" importlib-metadata = "1.7.0" pylint = "^2.6.0" sphinx_rtd_theme = "*" +hypothesis = "^6.8.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/unit/module_utils/test_nautobot_base_class.py b/tests/unit/module_utils/test_nautobot_base_class.py index 8f3dc4b0..7ea8092d 100644 --- a/tests/unit/module_utils/test_nautobot_base_class.py +++ b/tests/unit/module_utils/test_nautobot_base_class.py @@ -7,6 +7,7 @@ import pytest import os +from hypothesis import given, settings, HealthCheck, strategies as st from functools import partial from unittest.mock import patch, MagicMock, Mock from ansible.module_utils.basic import AnsibleModule @@ -355,3 +356,18 @@ def test_version_check_greater_equal_to_true(mock_module, obj_mock, version): def test_version_check_greater_equal_to_false(mock_module, obj_mock, version): mock_module.nb_object = obj_mock assert not mock_module._version_check_greater(version, "2.7", greater_or_equal=True) + + +@given(st.uuids(version=4)) +@settings(suppress_health_check=[HealthCheck(9)]) +def test_get_query_param_id_return_uuid(mock_module, value): + string_value = str(value) + data = mock_module._get_query_param_id("test", {"test": string_value}) + assert data == string_value + + +@given(st.integers()) +@settings(suppress_health_check=[HealthCheck(9)]) +def test_get_query_param_id_return_int(mock_module, value): + data = mock_module._get_query_param_id("test", {"test": value}) + assert data == value