From aa86d3dd0e3e28cfc390312cb94d3370f48ac417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Fri, 3 Nov 2023 23:09:33 +0100 Subject: [PATCH 1/2] core/endpoint: Get choices from PUT if POST missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the API token is not allowed to POST on a particular endpoint, choices won't be returned in `.actions.POST`, but we can still get them in `.actions.PUT`. This commit falls back to getting choices from `.actions.PUT` instead of raising an exception when `.actions.POST` is missing. Signed-off-by: Benoît Knecht --- pynetbox/core/endpoint.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pynetbox/core/endpoint.py b/pynetbox/core/endpoint.py index dfd989e..a0f7bef 100644 --- a/pynetbox/core/endpoint.py +++ b/pynetbox/core/endpoint.py @@ -587,9 +587,10 @@ def choices(self): token=self.api.token, http_session=self.api.http_session, ).options() - try: - post_data = req["actions"]["POST"] - except KeyError: + + actions = req.get("actions", {}) + post_data = actions.get("POST") or actions.get("PUT") + if post_data is None: raise ValueError( "Unexpected format in the OPTIONS response at {}".format(self.url) ) From 0f9a4632893b2f29210c94abbabf78b5b40e20c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Knecht?= Date: Mon, 30 Sep 2024 14:13:32 +0200 Subject: [PATCH 2/2] core/endpoint: Add unit tests for endpoint choices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we can get choices from the `PUT` action when the API doesn't allow `POST`, and that the `POST` data takes precedence over the `PUT` data. Signed-off-by: Benoît Knecht --- tests/unit/test_endpoint.py | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/unit/test_endpoint.py b/tests/unit/test_endpoint.py index 344b116..fe6a495 100644 --- a/tests/unit/test_endpoint.py +++ b/tests/unit/test_endpoint.py @@ -60,6 +60,59 @@ def test_choices(self): self.assertEqual(choices["letter"][1]["display_name"], "B") self.assertEqual(choices["letter"][1]["value"], 2) + def test_choices_put(self): + with patch("pynetbox.core.query.Request.options", return_value=Mock()) as mock: + api = Mock(base_url="http://localhost:8000/api") + app = Mock(name="test") + mock.return_value = { + "actions": { + "PUT": { + "letter": { + "choices": [ + {"display_name": "A", "value": 1}, + {"display_name": "B", "value": 2}, + {"display_name": "C", "value": 3}, + ] + } + } + } + } + test_obj = Endpoint(api, app, "test") + choices = test_obj.choices() + self.assertEqual(choices["letter"][0]["display_name"], "A") + self.assertEqual(choices["letter"][0]["value"], 1) + + def test_choices_precedence(self): + with patch("pynetbox.core.query.Request.options", return_value=Mock()) as mock: + api = Mock(base_url="http://localhost:8000/api") + app = Mock(name="test") + mock.return_value = { + "actions": { + "POST": { + "letter": { + "choices": [ + {"display_name": "A", "value": 1}, + {"display_name": "B", "value": 2}, + {"display_name": "C", "value": 3}, + ] + } + }, + "PUT": { + "letter": { + "choices": [ + {"display_name": "D", "value": 4}, + {"display_name": "E", "value": 5}, + {"display_name": "F", "value": 6}, + ] + } + }, + } + } + test_obj = Endpoint(api, app, "test") + choices = test_obj.choices() + self.assertEqual(choices["letter"][2]["display_name"], "C") + self.assertEqual(choices["letter"][2]["value"], 3) + def test_get_with_filter(self): with patch( "pynetbox.core.query.Request._make_call", return_value=Mock()