From 2c8a9181df9133b365fe8a1bd36af75c153e12a1 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 20 Mar 2020 14:25:00 +0100 Subject: [PATCH] Add support for not including the ID in creation requests Some of the PUT APIs don't like it when the ID is included in both the URI and the Body Change-Id: Ia99c77b5e3734f645d97d61920273652bdadae7b --- openstack/resource.py | 6 +++++ openstack/tests/unit/test_resource.py | 36 ++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/openstack/resource.py b/openstack/resource.py index f48ef2331..f2a9ba598 100644 --- a/openstack/resource.py +++ b/openstack/resource.py @@ -436,6 +436,8 @@ class Resource(dict): requires_id = True #: Whether create requires an ID (determined from method if None). create_requires_id = None + #: Whether create should exclude ID in the body of the request. + create_exclude_id_from_body = False #: Do responses for this resource have bodies has_body = True #: Does create returns a body (if False requires ID), defaults to has_body @@ -1261,6 +1263,10 @@ class Resource(dict): requires_id = (self.create_requires_id if self.create_requires_id is not None else self.create_method == 'PUT') + + if self.create_exclude_id_from_body: + self._body._dirty.discard("id") + if self.create_method == 'PUT': request = self._prepare_request(requires_id=requires_id, prepend_key=prepend_key, diff --git a/openstack/tests/unit/test_resource.py b/openstack/tests/unit/test_resource.py index 3fc701bdd..bc0605c18 100644 --- a/openstack/tests/unit/test_resource.py +++ b/openstack/tests/unit/test_resource.py @@ -1045,6 +1045,25 @@ class TestResource(base.TestCase): self.assertEqual({"x": body_value, "id": the_id}, result.body) self.assertEqual({"y": header_value}, result.headers) + def test__prepare_request_with_id_marked_clean(self): + class Test(resource.Resource): + base_path = "/something" + body_attr = resource.Body("x") + header_attr = resource.Header("y") + + the_id = "id" + body_value = "body" + header_value = "header" + sot = Test(id=the_id, body_attr=body_value, header_attr=header_value, + _synchronized=False) + sot._body._dirty.discard("id") + + result = sot._prepare_request(requires_id=True) + + self.assertEqual("something/id", result.url) + self.assertEqual({"x": body_value}, result.body) + self.assertEqual({"y": header_value}, result.headers) + def test__prepare_request_missing_id(self): sot = resource.Resource(id=None) @@ -1401,7 +1420,8 @@ class TestResourceActions(base.TestCase): self.session.get_endpoint_data.return_value = self.endpoint_data def _test_create(self, cls, requires_id=False, prepend_key=False, - microversion=None, base_path=None, params=None): + microversion=None, base_path=None, params=None, + id_marked_dirty=True): id = "id" if requires_id else None sot = cls(id=id) sot._prepare_request = mock.Mock(return_value=self.request) @@ -1411,6 +1431,9 @@ class TestResourceActions(base.TestCase): result = sot.create(self.session, prepend_key=prepend_key, base_path=base_path, **params) + id_is_dirty = ('id' in sot._body._dirty) + self.assertEqual(id_marked_dirty, id_is_dirty) + sot._prepare_request.assert_called_once_with( requires_id=requires_id, prepend_key=prepend_key, base_path=base_path) @@ -1439,6 +1462,17 @@ class TestResourceActions(base.TestCase): self._test_create(Test, requires_id=True, prepend_key=True) + def test_put_create_exclude_id(self): + class Test(resource.Resource): + service = self.service_name + base_path = self.base_path + allow_create = True + create_method = 'PUT' + create_exclude_id_from_body = True + + self._test_create(Test, requires_id=True, prepend_key=True, + id_marked_dirty=False) + def test_put_create_with_microversion(self): class Test(resource.Resource): service = self.service_name