diff --git a/pynautobot/core/api.py b/pynautobot/core/api.py index 0dd6596f..95be61f1 100644 --- a/pynautobot/core/api.py +++ b/pynautobot/core/api.py @@ -13,7 +13,7 @@ # limitations under the License. # # This file has been modified by NetworktoCode, LLC. - +from functools import cached_property from packaging import version import requests from requests.adapters import HTTPAdapter @@ -110,15 +110,13 @@ def __init__( self.users = App(self, "users") self.plugins = PluginsApp(self) self.graphql = GraphQLQuery(self) - self._validate_version() - def _validate_version(self): + def _validate_version(self, api_version: str) -> None: """Validate API version if eq or ge than 2.0 raise an error.""" - api_version = self.version if api_version.replace(".", "").isnumeric() and version.parse(api_version) < version.parse("2.0"): raise ValueError("Nautobot version 1 detected, please downgrade pynautobot to version 1.x") - @property + @cached_property def version(self): """Retrieves the version of the Nautobot REST API that the connected instance is using. @@ -142,12 +140,15 @@ def version(self): '1.0' """ - return Request( + v = Request( base=self.base_url, http_session=self.http_session, api_version=self.api_version, token=self.token, ).get_version() + self._validate_version(v) + + return v def openapi(self): """Retrieves the OpenAPI specification (OAS) document for the connected Nautobot instance. diff --git a/tests/test_api.py b/tests/test_api.py index bb9c142f..fb2201f3 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -69,7 +69,7 @@ class ResponseHeadersWithVersion: ) def test_api_version(self, *_): with self.assertRaises(ValueError) as error: - pynautobot.api(host) + pynautobot.api(host).version self.assertEqual( str(error.exception), "Nautobot version 1 detected, please downgrade pynautobot to version 1.x" ) @@ -124,6 +124,31 @@ def test_api_status(self, *_): self.assertEqual(api.status()["nautobot-version"], "1.3.2") +class ApiLazyLoadVersionTestCase(unittest.TestCase): + """ + Tests that version is lazy loaded on the first API call, rather than on instantiation + """ + + @patch("urllib3.connectionpool.HTTPConnectionPool._get_conn") + def test_lazy_load_version(self, getconn_mock): + getconn_mock.return_value.getresponse.side_effect = [ + Mock(status=200, msg=HTTPMessage()), + ] + + api = pynautobot.api( + "http://any.url/", + retries=2, + ) + + assert getconn_mock.return_value.request.mock_calls == [] + + api.version + + assert getconn_mock.return_value.request.mock_calls == [ + call("GET", "/api/", body=None, headers=ANY), + ] + + class ApiRetryTestCase(unittest.TestCase): class ResponseWithStatus: ok = False @@ -137,12 +162,10 @@ def test_api_retry(self, getconn_mock): Mock(status=200, msg=HTTPMessage()), ] - api = pynautobot.api( + pynautobot.api( "http://any.url/", retries=2, - ) - with patch("pynautobot.api.version", "2.0"): - api.version + ).version assert getconn_mock.return_value.request.mock_calls == [ call("GET", "/api/", body=None, headers=ANY),